Building Hash by grouping array of objects based on a property of the items
10,450
Solution 1
Ruby has anticipated your need, and has got you covered with Enumerable#group_by
:
irb(main):001:0> aers = %w(a b c d ab bc de abc)
#=> ["a", "b", "c", "d", "ab", "bc", "de", "abc"]
irb(main):002:0> aers.group_by{ |s| s.size }
#=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
In Ruby 1.9, you can make this even shorter with:
irb(main):003:0> aers.group_by(&:size)
#=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
Solution 2
Phrogz is correct, group_by is there for the taking. Your code contains one of ruby's gotcha's.
aers = %w(a b c d ab bc de abc)
sh = Hash.new([]) # returns the _same_ array everytime the key is not found.
# sh = Hash.new{|h,v| h[v] = []} # This one works
p sh, sh.default
aers.each do |aer|
sh[aer.size] << aer #modifies the default [] every time
end
p sh, sh.default
p sh[5]
Output
{}
[]
{}
["a", "b", "c", "d", "ab", "bc", "de", "abc"]
["a", "b", "c", "d", "ab", "bc", "de", "abc"]
Solution 3
You can also accomplish this by chaining methods... which might only be of academic interest for this problem, but is still a good technique to be familiar with.
irb(main):017:0> sh = {}
=> {}
irb(main):018:0> aers.collect{|k| k.size}.uniq!.each{|k| sh[k] = aers.select{|j| j.size == k}}
=> [1, 2, 3]
irb(main):019:0> sh
=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
irb(main):020:0>
Author by
user141146
Updated on June 06, 2022Comments
-
user141146 about 2 years
I'm wondering if there is a more canonical way to do this in ruby 1.9
I have an array with a bunch of objects and I want to group them into a Hash using a property of each object in the array.
Very simplified example:
> sh = {} => {} > aers = %w(a b c d ab bc de abc) => ["a", "b", "c", "d", "ab", "bc", "de", "abc"] > aers.each do |aer| > sh[aer.size] = [] if sh[aer.size].nil? > sh[aer.size] << aer > end => ["a", "b", "c", "d", "ab", "bc", "de", "abc"] > sh => {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
I tried this, but its output is wrong (as you can see):
sh = Hash.new([]) => {} > aers.each do |aer| > sh[aer.size] << aer > end => ["a", "b", "c", "d", "ab", "bc", "de", "abc"] > sh => {}
-
PJP over 13 yearsThis is why Ruby is so cool; It knows what we want even before we do.
-
Claudio Acciaresi over 13 yearsPlease not that in 1.8 symbol to proc is much slower than just using the block. confreaks.net/videos/… check 31:18. In 1.9 they are not differences in performance.