2011-01-14 3 views
11

Я интересно, если есть более канонический способ сделать это в рубин 1,9Строительство Hash, группируя массив объектов, основанный на свойстве предметов

У меня есть массив с кучей объектов, и я хочу сгруппировать их в хэш, используя свойство каждого объекта в массиве.

Очень упрощенный пример:

> 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 
=> {} 
+2

И причина ваш код не работает, как ожидается, будет объяснено здесь: http://stackoverflow.com/questions/2698460/strange-ruby-behavior-when-using -hash-new и здесь: http://stackoverflow.com/questions/2552579/ruby-method-array-not-updating-the-array-in-hash (довольно распространенная ошибка в Ruby). –

ответ

30

Рубин предвосхитил свою потребность, и есть ты покрытый 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"]} 

В R убий 1.9, вы можете сделать это еще короче с:

irb(main):003:0> aers.group_by(&:size) 
#=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]} 
+3

Вот почему Ruby так крут; Он знает, чего мы хотим, даже до того, как мы это сделаем. –

+0

Пожалуйста, не то, чтобы в символе 1.8 для proc было намного медленнее, чем просто использование блока. http://confreaks.net/videos/427-rubyconf2010-zomg-why-is-this-code-so-slow check 31:18. В 1.9 они не отличаются от производительности. –

1

Вы также можете сделать это с помощью цепочки методов ..., которые могут быть лишь академический интерес для этой проблемы, но все еще хороший метод, чтобы быть знакомым с ,

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> 
3

Phrogz правильный, group_by есть для взятия. В вашем коде содержится одна из них.

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"] 
Смежные вопросы