Ruby program to create even_odd method that accepts the whole number - ruby

I need help to write even_odd method that accepts an array of whole number.
It should return an array of 2 arrays
The first nested array should contain only the odd numbers
The second nested array should contain only the even numbers
If there are no even or odd numbers, the respective inner array should be empty
Output should look like this : -
even_odd([3, 5, 8, 2, 4, 6])
[[3, 5], [2, 4, 6, 8]]
even_odd([3, 5])
[[3, 5], []]
even_odd([2, 4])
[[], [2, 4]]
I am new to ruby programming, I have tried below but not getting the result :-
def even_odd(numbers)
arr1, arr2 = []
idx = 0
while idx < numbers.length
if numbers[idx] % 2 == 0
puts arr1[idx]
elsif
puts arr2[idx]
end
idx += 1
end
end
puts even_odd([2, 3, 6])
Error :-
main.rb:6:in `even_odd': undefined method `[]' for nil:NilClass (NoMethodError)
from main.rb:13:in `<main>'

I would do this
def even_odd(numbers)
numbers.sort.partition(&:odd?)
end
even_odd([3, 5, 8, 2, 4, 6])
# => [[3, 5], [2, 4, 6, 8]]
even_odd([3, 5])
# => [[3, 5], []]
even_odd([2, 4])
# => [[], [2, 4]]

puts is a print statement in ruby, not an append one. It also doesn't run a function/method. You'll also want to call the index on the numbers array inside the if...else block.
This should do the trick:
def even_odd(numbers)
arr1, arr2 = [], []
idx = 0
while idx < numbers.length
if numbers[idx] % 2 == 0
arr1 << numbers[idx]
elsif
arr2 << numbers[idx]
end
idx += 1
end
return arr1, arr2
end
arrays = even_odd([2, 3, 6])
puts arrays

Related

How do I find the location of an integer in an array of arrays in ruby?

Given:
a = [[1,2,3,4],
[1,2,3,7],
[1,2,3,4]]
What do I need to do to output the location of the 7 as (1,3)?
I've tried using .index to no avail.
require 'matrix'
a = [[1, 2, 3, 4],
[1, 2, 3, 7],
[1, 2, 3, 4]]
Matrix[*a].index(7)
=> [1, 3]
If your sub-arrays are all the same width, you can flatten it into a single array and think of the position as row_num * row_width + col_num:
idx = a.flatten.index(7)
row_width = a[0].length
row = idx / row_width
col = idx - (row * row_width)
puts [row, col] # => [1, 3]
Or you could just iterate it to find all matches:
def find_indices_for(array, value)
array.with_object([]).with_index do |(row, matches), row_index|
matches << [row_index, row.index(value)] if row.index(value)
end
end
find_indices_for(a, 7) # => [[1, 3]]
find_indices_for(a, 2) # => [[0, 1], [1, 1], [2, 1]]
each_with_index works pretty well here:
def locator(array, number)
locations = Array.new
array.each_with_index do |mini_array, index|
mini_array.each_with_index do |element, sub_index|
locations << [index, sub_index] if element == number
end
end
locations
end
Now, locator(array, number) will return an array of containing all the locations of number in array.
def locs2D(a,e)
a.size.times.with_object([]) do |row,arr|
row.size.times { |col| arr << [row,col] if a[row][col] == e }
end
end
locs2D(a,7) #=> [[1, 3]]
locs2D(a,3) #=> [[0, 2], [1, 2], [2, 2]]

Assigning values to a 2D array using "each" method

I'm trying to transpose [[0, 1, 2], [3, 4, 5], [6, 7, 8]]. I get [[2, 5, 8], [2, 5, 8], [2, 5, 8]].
I can see what is happening with the line p transposed_arr but do not understand why this is happening. At every iteration it changes every row instead of only one.
def my_transpose(arr)
# number of rows
m = arr.count
#number of columns
n = arr[0].count
transposed_arr = Array.new(n, Array.new(m))
# loop through the rows
arr.each_with_index do |row, index1|
# loop through the colons of one row
row.each_with_index do |num, index2|
# swap indexes to transpose the initial array
transposed_arr[index2][index1] = num
p transposed_arr
end
end
transposed_arr
end
You need to make only one wee change and your method will work fine. Replace:
transposed_arr = Array.new(n, Array.new(m))
with:
transposed_arr = Array.new(n) { Array.new(m) }
The former makes transposed_arr[i] the same object (an array of size m) for all i. The latter creates a separate array of size m for each i
Case 1:
transposed_arr = Array.new(2, Array.new(2))
transposed_arr[0].object_id
#=> 70235487747860
transposed_arr[1].object_id
#=> 70235487747860
Case 2:
transposed_arr = Array.new(2) { Array.new(2) }
transposed_arr[0].object_id
#=> 70235478805680
transposed_arr[1].object_id
#=> 70235478805660
With that change your method returns:
[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]

Transposing rows to columns (with limitations on what methods I can use)

I'm doing a practice problem and the question is to transpose rows to columns in Ruby.
I understand while loops are "rookie-esque" in Ruby, but I think they prefer I use basic methods and control flow statements: while, each, map. Obviously, I can't use Array#transpose. They want me to basically write Array#transpose from scratch.
It keeps telling me:
undefined method `[]=' for nil:NilClass (NoMethodError)
Here is my code:
def transpose(rows)
idx1 = 0
cols = []
while idx1 < rows.count
idx2 = 0
while idx2 < rows[idx1].count
cols[idx1][idx2] = rows[idx2][idx1]
idx2 += 1
end
idx1 += 1
end
return cols
end
puts transpose(rows = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
])
Perhaps this will help you:
def transpose(rows)
idx1 = 0 # => 0
cols = [] # => []
while idx1 < rows.count # => true
idx2 = 0 # => 0
while idx2 < rows[idx1].count # => true
cols[idx1][idx2] = rows[idx2][idx1] # ~> NoMethodError: undefined method `[]=' for nil:NilClass
idx2 += 1
end
idx1 += 1
end
return cols
end
puts transpose(rows = [
[0, 1, 2], # => [0, 1, 2]
[3, 4, 5], # => [3, 4, 5]
[6, 7, 8] # => [6, 7, 8]
])
# ~> NoMethodError
# ~> undefined method `[]=' for nil:NilClass
That was created using "Seeing Is Believing" in Sublime Text 2.
Breaking it down, here's where you're going wrong:
cols = [] # => []
cols[0] # => nil
cols[0][0] # ~> NoMethodError: undefined method `[]' for nil:NilClass
You can't use a sub-index on a nil. The first index doesn't exist in the empty array.
In your outer while loop you need to make cols[idx1] an empty array, else it is nil in your inner while loop:
while idx1 < rows.count
cols[idx1] = []
idx2 = 0
while idx2 < rows[idx1].count
# ...
end
end
You could do that using Array#new with a block:
Array.new(rows.size) { |i| Array.new(rows.size) { |j| rows[j][i] } }
#=> [[0, 3, 6], [1, 4, 7], [2, 5, 8]]
Alternatively, you could use methods from the class Matrix, for which you need:
require 'matrix'
Here's an easy way that does not use Array#transpose, though it may not satisfy the spirit of the question:
Matrix[*rows].transpose.to_a
#=> [[0, 3, 6], [1, 4, 7], [2, 5, 8]]
However, you could do this:
Matrix.build(rows.size) { |i,j| rows[j][i] }.to_a
#=> [[0, 3, 6], [1, 4, 7], [2, 5, 8]]
Another way to do this for comparison using nested Enumerable#maps:
rows = [[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]
def my_transpose(matrix)
matrix.first.size.times.map { |column| matrix.map { |row| row[column] } }
end
my_transpose(rows)
# => [[0, 3, 6], [1, 4, 7], [2, 5, 8]]
my_transpose(my_transpose(rows))
# => [[0, 1, 2], [3, 4, 5], [6, 7, 8]]

Operate on array elements without changing index

I'm trying to operate on certain elements of an array while referencing their index in the block. Operating on the whole array is easy
arr = [1, 2, 3, 4, 5, 6, 7, 8]
arr.each_with_index { |num, index| puts "#{num}, "#{index}" }
But what if I want to work just with elements 4, 6 to return
4, 3
6, 5
I can create a new array composed of certain elements of the original and run the block on that, but then the index changes.
How can I select the elements and their index?
Just put a condition on it:
indice = [3, 5]
arr.each_with_index do
|num, index| puts "#{num}, #{index}" if indice.include?(index)
end
This is another style:
indice = [3, 5]
arr.each_with_index do
|num, index|
next unless indice.include?(index)
puts "#{num}, #{index}"
end
I cannot tell from the question whether you are given values in the array and want to obtain their indices, or vice-versa. I therefore will suggest one method for each task. I will use this array for examples:
arr = [1, 2, 3, 4, 5, 6, 7, 8]
Values to Indices
If you are given values:
vals = [4, 6]
you can retrieve the number-index pairs like this:
vals.map { |num| [num, arr.index(num)] }
#=> [[4, 3], [6, 5]]
or print them directly:
vals.each { |num| puts "#{num}, #{arr.index(num)}" }
# 4, 3
# 6, 5
#=> [4, 6]
If an element of vals is not present in arr:
vals = [4, 99]
vals.map { |num| [num, arr.index(num)] }
#=> [[4, 3], [99, nil]]
Indices to Values
If you are given indices:
indices = [3, 5]
you can retrieve the index-value pairs like this:
indices.zip(arr.values_at(*indices))
#=> [[3, 4], [5, 6]]
and then print in whatever format you like.
If an index is out-of-range, nil will be returned:
indices.zip(arr.values_at(*[3, 99]))
#=> [[3, 4], [5, nil]]

Ruby - protect a variable while iterating through a loop

I'm trying to perform an action with a nested array through a loop. The loop executes once but then I get a nomethod error because the variable is not reset.
array = [[9, 2, 0, 0], [4, 1, 2, 2], [7, 1, 5, 5], [6, 1, 3, 1]]
comments = [[0, 0, 0], [1, 1, 1], [2, 2, 2]]
def shift_comments(array)
array.each {|x| x.shift}
end
def map_distance_coordinants(array)
array2 = array.map {|x,y| [Math.sqrt(x*x + y*y)]}
array2
end
def input_is_comment_format(array, comments)
distance_coordinants = shift_comments(comments)
mapped_coordinanats = map_distance_coordinants(distance_coordinants)
print mapped_coordinanats
print comments
end
i = 0
while i < array.length
input_is_comment_format(array[i], comments)
i += 1
end
Returns:
[[0.0], [1.4142135623730951], [2.8284271247461903]][[0, 0], [1, 1], [2, 2]]
temp4.rb:9:in `block in map_distance_coordinants': undefined method `*' for nil:NilClass (NoMethodError)
How do I protect 'comments' so that I can use it for each iteration of the loop? Thanks.
You could use dup:
input_is_comment_format(array[i], comments.dup)
so you have a copy of the array to work with and your original array won't be modified.

Resources