Building Hash путем группировки массива объектов на основе свойства элементов
Мне интересно, есть ли более канонический способ сделать это в ruby 1.9
У меня есть массив с кучей объектов, и я хочу сгруппировать их в хэш, используя свойство каждого объекта в массиве.
Очень упрощенный пример:
> 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"]}
Я пробовал это, но его вывод неверен (как вы можете видеть):
sh = Hash.new([])
=> {}
> aers.each do |aer|
> sh[aer.size] << aer
> end
=> ["a", "b", "c", "d", "ab", "bc", "de", "abc"]
> sh
=> {}
Ответы
Ответ 1
Ruby предвосхитил вашу потребность, и у вас есть 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"]}
В Ruby 1.9 вы можете сделать это еще короче:
irb(main):003:0> aers.group_by(&:size)
#=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
Ответ 2
Фрогз прав, group_by есть для взятия. Ваш код содержит один из ruby gotcha.
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]
Выход
{}
[]
{}
["a", "b", "c", "d", "ab", "bc", "de", "abc"]
["a", "b", "c", "d", "ab", "bc", "de", "abc"]
Ответ 3
Вы также можете выполнить это путем цепочки методов... которые могут быть только академическим интересом для этой проблемы, но по-прежнему хорошая техника для ознакомления.
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>