Reputation: 4171
I have a loop building a hash for use in a select field. The intention is to end up with a hash:
{ object.id => "object name", object.id => "object name" }
Using:
@hash = {}
loop_over.each do |ac|
@hash[ac.name] = ac.id
end
I think that the map
method is meant for this type of situation but just need some help understanding it and how it works. Is map
the right method to refactor this each
loop?
Upvotes: 1
Views: 360
Reputation: 434685
Data transformations like this are better suited to each_with_object
:
@hash = loop_over.each_with_object({}) { |ac, h| h[ac.name] = ac.id }
If your brain is telling you to use map
but you don't want an array as the result, then you usually want to use each_with_object
. If you want to feed the block's return value back into itself, then you want inject
but in cases like this, inject
requires a funny looking and artificial ;h
in the block:
@hash = loop_over.inject({}) { |h, ac| h[ac.name] = ac.id; h }
# -------------------- yuck -----------------------------^^^
The presence of the artificial return value is the signal that you want to use each_with_object
instead.
Upvotes: 4
Reputation: 106932
I would go for the inject
version, but use update
in the block to avoid the easy to miss (and therefore error prone) ;h
suffix:
@hash = loop_over.inject({}) { |h, ac| h.update(ac.name: ac.id) }
Upvotes: 0
Reputation: 742
Ruby 2.1.0 introduces brand new method to generate hashes:
h = { a: 1, b: 2, c: 3 }
h.map { |k, v| [k, v+1] }.to_h # => {:a=>2, :b=>3, :c=>4}
Upvotes: 0
Reputation: 13014
You can simply do this by injecting a blank new Hash
and performing your operation:
loop_over.inject({}){ |h, ac| h[ac.name] = ac.id; h }
Ruby FTW
Upvotes: 1
Reputation: 44685
Try:
Hash[loop_over.map { |ac| [ac[:name], ac[:id]] }]
Or if you are running on Ruby 2:
loop_over.map { |ac| [ac[:name], ac[:id]] }.to_h
Upvotes: 3
Reputation: 14900
@hash = Hash[loop_over.map { |ac| {ac.name => ac.id} }.map(&:flatten)]
Edit, a simpler solution as per suggestion in a comment.
@hash = Hash[ loop_over.map { |ac| [ac.name, ac.id] } ]
Upvotes: 1
Reputation: 59631
No a map isn't the correct tool for this.
The general use-case of a map
is to take in an array, perform an operation on each element, and spit out a (possibly) new array (not a hashmap) of the same length, with the individual element modifications.
Here's an example of a map
x = [1, 2, 3, 4].map do |i|
i+1 #transform each element by adding 1
end
p x # will print out [2, 3, 4, 5]
Your code:
@hash = {}
loop_over.each do |ac|
@hash[ac.name] = ac.id
end
There is nothing wrong with this example. You are iterating over a list, and populating a hashmap exactly as you wished.
Upvotes: 0