This has been asked before, but I can't find an answer that works. I have the following code:
[[13,14,16,11],[22,23]].each do |key,value|
puts key
end
It should in theory print:
0
1
But instead it prints:
13
22
Why does ruby behave this way?
Why does ruby behave this way?
It's because what actually happens internally, when each and other iterators are used with a block instead of a lambda, is actually closer to this:
do |key, value, *rest|
puts key
end
Consider this code to illustrate:
p = proc do |key,value|
puts key
end
l = lambda do |key,value|
puts key
end
Using the above, the following will set (key, value) to (13, 14) and (22, 23) respectively, and the above-mentioned *rest as [16, 11] in the first case (with rest getting discarded):
[[13,14,16,11],[22,23]].each(&p)
In contrast, the following will spit an argument error, because the lambda (which is similar to a block except when it comes to arity considerations) will receive the full array as an argument (without any *rest as above, since the number of arguments is strictly enforced):
[[13,14,16,11],[22,23]].each(&l) # wrong number of arguments (1 for 2)
To get the index in your case, you'll want each_with_index as highlighted in the other answers.
Related discussions:
Proc.arity vs Lambda.arity
Why does Hash#select and Hash#reject pass a key to a unary block?
You can get what you want with Array's each_index' method which returns the index of the element instead of the element itself. See [Ruby'sArray` documentation]1 for more information.
When you do:
[[13,14,16,11],[22,23]].each do |key,value|
before the first iteration is done it makes an assignment:
key, value = [13,14,16,11]
Such an assignment will result with key being 13 and value being 14. Instead you should use each_with_index do |array, index|. This will change the assignment to:
array, index = [[13,14,16,11], 0]
Which will result with array being [13,14,16,11] and index being 0
You have an array of arrays - known as a two-dimensional array.
In your loop, your "value" variable is assigned to the first array, [13,14,16,11]
When you attempt to puts the "value" variable, it only returns the first element, 13.
Try changing puts value to puts value.to_s which will convert the array to a string.
If you want every value, then add another loop block to your code, to loop through each element within the "value" variable.
[[1,2,3],['a','b','c']].each do |key,value|
value.each do |key2,value2|
puts value2
end
end
Related
In this script:
dictionary = [
"below","down","go","going","horn","how","howdy","it","i","low","own","part",
"partner","sit"
]
def substrings(string, dictionary)
frequencies = Hash.new(0)
dictionary.each_index do |substring|
frequencies.store(dictionary.fetch(substring), string.scan(/#{dictionary[substring]}/i).length)
end
frequencies.each_pair {|word, count| puts "#{word} => #{count}"}
end
substrings("Howdy partner, sit down! How's it going?", dictionary)
if I change dictionary.each_index to dictionary.each, I get the following error:
in `fetch': no implicit conversion of String into Integer (TypeError)"
Please explain why. I understand that each returns the values of the array and that each_index returns the index. I cannot get the code to work using each and want to understand why.
Array#each_index iterates through each of the array. This means that for your dictionary array, the substring variable will be set to 0, then 1, then 2, etc. Each of these values is an Integer.
Array#fetch expects an Integer as it's argument and returns the value at that index in the array. When you use each, you are passing the actual string value instead of the index. Thus, the error you see.
If you want to use Array#each, you will need to update your each block like this.
dictionary.each do |substring|
frequencies.store(substring, string.scan(/#{substring}/i).length)
end
dictionary is an array. Array#fetch expects a numerical index and will fetch the value at that index. If you use dictionary.each then substring is going to be a string (ie. below, down, go, going, horn, etc.) which isn't a valid argument to the method.
each_index works because then substring is an integer (the index into the array).
Why does using 0 as the argument in each_with_object not return the correct value:
[1,2,3].each_with_object(0) {|i,o| o += i }
# => 0
but using an empty array and reduce(:+) does?
[1,2,3].each_with_object([]) {|i,o| o << i }.reduce(:+)
# => 6
From documentation it says:
each_with_object(obj) → an_enumerator
Iterates the given block for each element with an arbitrary object given, and returns the initially given object.
If no block is given, returns an enumerator.
As Array is an the same initial object, but with modified values is being returned in this case.
If we see the code of each_with_object, It is:
# File activesupport/lib/active_support/core_ext/enumerable.rb, line 79
def each_with_object(memo)
return to_enum :each_with_object, memo unless block_given?
each do |element|
yield element, memo
end
memo
end
You can see, It don't modifies memo, so if memo is 0 and you change it in the code block, It will still return zero, but if you pass [] and change it inside code block, It will return array with values.
There is no Ruby bug in your examples, which means they are all correct.
In the first example, the argument 0 is the return value. In the second example, the argument (that appears to be [] initially) is the return value. Only in the latter, the argument had been modified, and ended up looking different from what it looked like at the beginning, but the identity of the object is retained.
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.
I am new to Ruby and I am trying to write a method that groups an array of words into anagram groups. Here is the code:
def combine_anagrams(words)
dict = words.inject(Hash.new(0)) do |list,ws|
key = sort_word(ws)
if !list.has_key?(key)
list[key] = []
end
list[key].push(ws)
list #What is this
end
return dict.values
end
My question is what the statement list is for. If I take it out list becomes an array instead of hash.
Every method/block/etc. in Ruby returns something, and unless there is an early return statement, whatever the last statement in the method/block/etc. is, is what is returned.
In your case, having list be the last line in the block passed to inject ensures that list is returned by the block. When you remove it, the return value of list[key].push(ws) is returned, which obviously isn't what you want.
Note that this behavior also makes using the return keyword when it is the last statement that would be executed otherwise is unnecessary (this includes the return you have at the end of your method). Though some prefer to be explicit that they intend to return something and use them even when not needed.
On an unrelated note: your if !list.has_key?(key) can be rewritten unless list.has_key?(key).
inject works like this:
final = enumerable.inject(initial_value) do |current_value, iteration|
# calculations, etc. here
value # next iteration, current_value will be whatever the block returns
end
So, in your case, initial_value is Hash.new(0), or an empty Hash with 0 as the default value for a key that doesn't exist instead of nil. This is passed into the inject block for the first element in enumerable.
Inside the inject block, you check to see if key already exists as a key on the hash. If it does not, set it equal to an empty array. In either case, take the current iteration of words (ws) and push it onto the array.
Finally, the block returns the current version of list; it becomes current_value (the first parameter to the inject block) the next time the loop processes an element from enumerable.
As a more simple example, check out this sample:
numbers = [1, 2, 3, 4]
sum = inject(0) do |total, number| # first time, total will be 0
total + number # set total next time to be whatever total is now plus the current number
end
Take a look at http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-inject
In the inject method if you pass two arguments into it (in your case list and ws) the first one - list - is so-called accumulator value. The value which is returned by the inject block at each iteration step is assigned to the list variable. So the line with the only word "list" which you commented as "#What is this" is used for assigning the value of the list in the block to the "list" accumulator variable.
the statement "list" is the return value of the whole block. The line: "list[key] = []" has a return value of "list", therefore it doesnt need another line to set the return value of the if condition to 'list', but the return value of list[key].push(ws) is list[key]. we want to get the updated value of list in the end, therefore we need to return that value from the block each time, so that further processing acts of the updated list, and not something else.
As a background, each ruby line also has a return value, so if that were the last line of a block, or a function, it automatically becomes the return value of the whole block or the function respectively.
To understand this further, try some code like this in irb:
a = [1,2,3,4,5]
b = a.inject(0) {|sum, val| puts sum; puts val; sum + val}
the inner block comprises of three statememts; the last statement returns the value of sum+val to the block, which get stored in sum, to be used in next iterations.
Also, try some code like this:
h = {:a => []}
b = h[:a].push 6
See what b evaluates to; in your code, you need 'b' to be the accumulated hash, and not the array that is stored in h[:a]
I've recently started coding Ruby and I'm having a mis-understanding with block parameters.
Take the following code for example:
h = { # A hash that maps number names to digits
:one => 1, # The "arrows" show mappings: key=>value
:two => 2 # The colons indicate Symbol literals
}
h[:one] # => 1. Access a value by key
h[:three] = 3 # Add a new key/value pair to the hash
h.each do |key,value| # Iterate through the key/value pairs
print "#{value}:#{key}; " # Note variables substituted into string
end # Prints "1:one; 2:two; 3:three; "
I understand the general hash functionality, however I don't understand how value and key are set to anything. They are specified as parameters in the block, but the hash is never associated in any way with these parameters.
This is the Ruby block (Ruby's name for an anonymous function) syntax. And key, value are nothing but the arguments passed to the anonymous function.
Hash#each takes one parameter: A function which has 2 parameters, key and value.
So if we break it down into parts, this part of your code: h.each, is calling the each function on h. And this part of your code:
do |key, value| # Iterate through the key/value pairs
print "#{value}:#{key}; " # Note variables substituted into string
end # Prints "1:one; 2:two; 3:three;
is the function passed to each as an argument and key, value are arguments passed to this function. It doesn't matter what you name them, first argument expected is key and second argument expected is value.
Lets draw some analogies. Consider a basic function:
def name_of_function(arg1, arg1)
# Do stuff
end
# You'd call it such:
foo.name_of_function bar, baz # bar is becomes as arg1, baz becomes arg2
# As a block:
ooga = lambda { |arg1, arg2|
# Do stuff
}
# Note that this is exactly same as:
ooga = lambda do |arg1, arg2|
# Do stuff
end
# You would call it such:
ooga.call(bar, baz) # bar is becomes as arg1, baz becomes arg2
So your code can also be written as:
print_key_value = lambda{|arg1, arg2| print "#{arg1}:#{arg2}"}
h = {
:one => 1,
:two => 2
}
h.each &print_key_value
There are multiple ways in which the code inside a block can be executed:
yield
yield key, value # This is one possible way in which Hash#each can use a block
yield item
block.call
block.call(key, value) # This is another way in which Hash#each can use a block
block.call(item)
The hash (h) is associated with the loop due to you calling h.each rather than calling each on something else. It's effectively saying, "For each key/value pair in h, let the key iteration variable be the key, let the value iteration variable be the value, then execute the body of the loop."
If that doesn't help, have a look at this page on each... and if you can explain more about which bit you're finding tricky, we may be able to help more. (Well, others may be able to. I don't really know Ruby.)
The hash is indeed associated with these parameters because you call h.each to iterate over the hash:
h.each <- here's the link you are missing
Perhaps it's easier for you if you start with an array instead:
a = [1,2,3]
a.each do |v|
puts v
end
and play around with this first (each, each_with_index, ...)
when you call h.each, that's when you say that this is this specific h hash that you want to use for this each iteration.
Hence when you do that the value and key variables are assigned to the values in your hash, one by one.
I think the question is about the variable names. The names have no significance. Only the order matters. Within |...| inside each {...}, the key and the value are given in that order. Since its natural to assign the variable name key to key and value to value, you often find it done like that. In fact, it can be anything else.
each{|k, v| ...} # k is key, v is value
each{|a, b| ...} # a is key, b is value
or even misleadingly:
each{|value, key| ...} # value is key, key is value