Iterating Over Multidimensional Arrays in Ruby - ruby

I'm going over iteration within multidimensional arrays in Ruby on Codecademy and came across a question I can't seem to find the answer to. So, in their example, they show that a multidimensional array can be iterated using the following code:
things = [[1,2,3], ["red", "blue"]]
things.each do |sub_array|
sub_array.each do |item|
puts item
end
end
This prints out the values of both sub_arrays. However, if I only want to display one sub_array, how would I go about that? I have tried the following code but I'm getting an undefined method `each' for 2:Fixnum error.
things = [[1,2,3], ["red", "blue"]]
things.each do |numbers, colors|
colors.each { |item| puts item }
end
So, I guess my question is why my code is not functioning correctly and how I would go about printing out only the array at index 1?

Your block parameters deconstruct the array as follows:
The enumerator generated by :each yields each element of the outer array in sequence, and then applies the pattern matching based on the structure of the block parameters. So in the first iteration, you have [1,2,3] yielded to the block, which is then mapped to numbers = 1 and colors = 2. 3 is ignored because it doesn't fit the pattern.
If you only want to display one sub-array, you don't need iterate over the whole array- just grab the required element by the index (if you know what the index is):
things[1].each {|color| ... }
Or, you can assign it to a variable in a similar way. As long as you know the colors will always be in the second position, you can do this:
_, colors = *things
colors.each {|color| ... }

Related

Why isn't the following code working the way it should?

array = ["car","carrs"]
array.each { |x|
x.capitalize
}
I have tried doing with do too by removing the curly braces and adding do after .each, I have also tried for each in array, but that didnt work too. Am i doing something wrong because nothing gets capitalized?
String#capitalize returns a copy of the object with the first letter capitalized. What you're basically doing is looping through your array and generating new copies of the strings, but then immediately throwing them away.
You have a couple of ways to approach this:
You can use #map rather than #each to take each result of your loop block body and collect it into a new array:
array = ["car","carrs"]
capitalized_array = array.map { |x| x.capitalize }
Or, if you actually want to mutate the original strings, use String#capitalize! rather than capitalize, which mutates the input object, rather than returning a new object:
array = ["car","carrs"]
array.each { |x| x.capitalize! }
While it may seem tempting to use the mutative version, it is frequently a good idea to use non-mutative methods to produce transformations of your data, so you don't lose your original input data. Mutate-in-place can introduce subtle bugs by making the state of the data harder to reason about.
You have to understand the difference between map vs each. You can read it here.
For those who don't want to read that:
Each is like a more primitive version of map. It gives you every element so you can work with it, but it doesn’t collect the results. Each always returns the original, unchanged object. While map does the same thing, but. It returns a new array with the transformed elements.
So, you have to use map in order to return a new array:
array = ["car","carrs"]
capitalized_array = array.map { |x| x.capitalize }
# or
array = ["car","carrs"]
array.map! { |x| x.capitalize }
Now, what is the different between map and map!? We need to read the documentation
map invokes the given block once for each element of self. Creates a new array containing the values returned by the block. While map! invokes the given block once for each element of self, replacing the element with the value returned by the block.

Is there a better way?: iterating over an array in ruby

I'm working on a mini project for a summer class. I'd like some feedback on the code I have written, especially part 3.
Here's the question:
Create an array called numbers containing the integers 1 - 10 and assign it to a variable.
Create an empty array called even_numbers.
Create a method that iterates over the array. Place all even numbers in the array even_numbers.
Print the array even_numbers.
Here's my code, so far:
numbers = [1,2,3,4,5,6,7,8,9,10]
print numbers[3]
even_numbers.empty?
def even_numbers
numbers.sort!
end
Rather than doing explicit iteration, the best way is likely Array#select thus:
even_numbers = numbers.select { |n| n.even? }
which will run the block given on each element in the array numbers and produce an array containing all elements for which the block returned true.
or an alternative solution following the convention of your problem:
def get_even_numbers(array)
even_num = []
array.each do |n|
even_num << n if n.even?
end
even_num
end
and of course going for the select method is always preferred.

Ruby - How to access elements in a multi-dimensional array

I thought this was simple, but when it came down to this case I wasn't able to do it. I feel if I can understand this I will have a much better understanding of Ruby.
WHAT: I want to search inside a 2-d array of strings and integers, and return the index/indices of where a certain string is found. These indices of each subarray would also be placed in an array in the order of the corresponding subarrays.
Example when searching for the string "a":
Input array: [[1,"a","a",3],[1,"b"],["a",2]]
Output array: [[1,2],[],[0]]
WHAT I TRIED:
Intuitively I thought it would be something like:
source = [[1,"a","a",3],[1,"b"],["a",2]]
with
source.each.each_index.select { |v| v == "a" }
or
source.each {|x| x.each_index.select { |i| x[i] == "a" }}
QUESTIONS:
1) What should I call to get my output array from my input array?
2) I see so many other enumerators and methods get mashed together this way, how come I can't do it in this case? I don't want to clutter up this question with some of the simpler test cases I tried, but I either got undefined method errors or it would just return my source array.
3) Does it have to do with what blocks are associated with which methods? I modeled my code after the answer to question: Find indices of elements that match a given condition where I am confused why it seems like the block is directly associated with multiple methods. In other words, the |i| is from the #each_index while the boolean is for the #select. Seems random and disorganized to me right now how to structure these blocks (i.e why not vice versa?).
source.map { |row| row.each_index.select { |i| row[i] == "a" } }
# => [[1, 2], [], [0]]
You can't do it because your logic is mistaken :) source.each {...} will return source, doesn't matter what you do inside the block. To return the result, use map. source.each.each_index calls each_index on the enumerator returned by each without block, and that's not a method available on enumerators (you want each_index on arrays).
Indeed. each with block and each without block will do very different things.
Specifically, in my code above:
Start with source array. map with block will process the block for each element (called row), and return an array of the results. For each row, row.each_index without block will return an iterator of all indices of the row array, select with block on the iterator will return an array that will contain only some of those elements.

How to sort the strings within an array in Ruby

I'm just trying to sort my strings alphabetically but while maintaining their position inside an array. For example, I have :
myArray = ["tree", "house", "show", "merit", "timer"]
and I'd like to perform an each loop on it in order to output :
myArray = ["eert", "ehosu", "hosw", and so on...]
I wanted to do something like this :
myArray.each do |x|
x.chars.sort.join
end
For a single string that works but I guess "chars" doesn't work for multiple strings in an array. Or maybe it does and I'm not doing it right. Basically how would I modify it in order to get that output?
All you need in order to make this work is to call map on myArray, instead of each.
The map method will change each element to the result of running the block on the original element.
myArray = ["tree", "house", "show", "merit", "timer"]
myArray.map do |x|
x.chars.sort.join
end
# => ["eert", "ehosu", "hosw", "eimrt", "eimrt"]
Another thing to mention is that you are using camelCase for your variables, while the convention in Ruby is snake_case (my_array would be preferable).
You are sorting each element in the array. To do this, call the map function. I then broke down each element into multiple smaller array elements, and sorted that array. I transformed that array back into a sentence by using the join method.
myArray.map{|x|x.chars.sort{|x,y|x<=>y}.join}
=> ["eert", "ehosu", "hosw", "eimrt", "eimrt"]

Can't convert String onto integer (TypeError)

Following code return error "Can't convert String onto integer", please help
subject = ['eng','Math','Sci']
grade = ['grade 1','grade 2','grade 3']
subject.each do |sub|
puts ("some string")
grade[sub] .each do |grd|
puts ("some string")
end
end
grade[sub] .each do |grd| thats the problem.
Array elements are accessed by using a index of integer or a range of integers.
You are trying to access a array element by using the variable stored in sub. Since this is a ordinary .each loop it will loop all the elements in the array, in this case 'eng','Math','Sci'. If you want the position of for example 'eng' you could use a .each_with_index
it should probably just be
grade.each do |grd|
with each_with_index it would be
subject.each_with_index do |sub, index|
print sub
print grade[index]
end
If you want a subject -> grade collection it might be good to look into using a Hash like Dave Newton said.
{"eng" => "grade 1","Math" => "grade 2","Sci" => "grade 3"}.each do |subject, grade|
puts "#{subject| #{grade}"
end
When you do
grade[sub] .each do |grd|
Ruby expects sub to be using an integer to reference a position in the list of grades.
subject and grade are both arrays. They can only be accessed by their position. In your each loop, the block gets the actual element (in sub), not the position. So in line 5, you are trying to access grade['eng'], which will not work and produces the error. Note that unlike in e.g. PHP, an array and a hash (an associative array) are different things.
Guessing from your code, you might want to use each_index instead of each which will pass the index number to the block instead of the element.
I'm not sure I understand what you're trying to achieve; however, if you'd like to print subjects and grades and you're sure about the relative order of elements in the arrays, you could do this:
subject = ['eng','Math','Sci']
grade = ['grade 1','grade 2','grade 3']
subject.each_with_index do |sub, idx|
puts "#{sub} - #{grade[idx]}"
end
Output:
eng - grade 1
math - grade 2
sci - grade 3
An hash is however probably more suitable to your needs.

Resources