From: Brian Candler Date: 2008-11-20T21:10:51+09:00 Subject: Re: build hash by iterating Jason Lillywhite wrote: > range = (1..4) > sum = range.inject(0) {|result, element| result += element } That should be: range = (1..4) sum = range.inject(0) {|result, element| result + element } To understand this fully, I will write out what inject is doing in longhand: range = (1..4) tmp = 0 # the (0) bit range.each do |element| result = tmp tmp = result + element #(A)# ######(B)####### end sum = tmp # final value of inject #(B)# is the execution of the block body. #(A)# is done implicitly by 'inject': it stores the value calculated by the block, and then passes this into the next iteration, or else uses it as the final return value. > #However, iterating over a hash is confusing me. Here is a simple > example: > > hash = [[:diameter, 45], [:id, 2]].inject({}) do |result, element| > result[element.first] = element.last > result > end > > #can someone help me understand better what exactly is happening on each > iteration? Written out longhand as above: tmp = {} [[:diameter, 45], [:id, 2]].each do |element| result = tmp result[element.first] = element.last tmp = result #(A)# #(B)## end hash = tmp To start with the accumulator is set to an empty hash. After one iteration, you have done hash[:diameter] = 45, so you've added a new element to the hash. You then give the entire hash object as the value result from the block, so that it is passed in as 'result' to the next iteration. On the next iteration, you do hash[:id] = 2, so you've added a new value to it. But the same hash object is the result. In this case, for every iteration the *same* hash object is passed in, and returned so that it can be used by the following iteration. What you're doing is modifying that object as a side-effect of the block executing. Now, it is possible to get the same result without modifying the hash, but instead creating a new hash object in each iteration, like this: hash = [[:diameter, 45], [:id, 2]].inject({}) do |result, element| result.merge({element.first => element.last}) end This is what a 'functional' programmer would do, where functions cannot modify data, only create new data. In each iteration you're merging the hash built so far with a new one-element hash, to create a new partial result which is one element larger. This is less efficient, as you're repeatedly creating larger and larger hash objects only to be garbage-collected later. But if you were doing this in (say) Erlang, that's what you'd need to do. Hope this is a bit clearer now... Brian. -- Posted via http://www.ruby-forum.com/.