2012-01-21 6 views
0

Если у меня есть массив хэш, каждый с ключом дня:Как преобразовать массив хэшей в отсортированный хеш?

[ 
    {:day=>4,:name=>'Jay'}, 
    {:day=>1,:name=>'Ben'}, 
    {:day=>4,:name=>'Jill'} 
] 

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

{ 
    :1=>[{:day=>1,:name=>'Ben'}], 
    :4=>[{:day=>4,:name=>'Jay'},{:day=>4,:name=>'Jill'}] 
} 

I использую рубин 1.9.2 и Rails 3.1.1

+0

Хеш по природе не сортируются. Ruby 1.9+ поддерживает порядок вставки, поэтому, если вы создадите хэш и вставляете элементы на основе некоторого порядка сортировки, Ruby сохранит их. Однако любые последующие элементы не будут сортироваться и будут добавлены вместо этого. Если вам нужно получить их в определенном порядке, вы можете сортировать ключи до получения значений или вы можете поддерживать массив ключей параллельно хешу и сохранять этот массив в том порядке, который вы хотите, а затем итерации этот массив или использовать его с 'values_at' для извлечения значений в том порядке, в котором вы хотите. –

ответ

4

Лично я бы не стал «сортировать» ключи (что равно количеству заказов на время в Ruby 1.9), пока мне не понадобится. Затем вы можете использовать group_by:

arr = [{:day=>4,:name=>'Jay'}, {:day=>1,:name=>'Ben'}, {:day=>4,:name=>'Jill'}] 
arr.group_by { |a| a[:day] } 
=> {4=>[{:day=>4, :name=>"Jay"}, {:day=>4, :name=>"Jill"}], 
    1=>[{:day=>1, :name=>"Ben"}]} 

Вместо сортировки ключи, когда вы на самом деле необходимо.

+0

+1 это потрясающе. – Anurag

1

Предполагая, что вы массив называется это list, вот один из способов, используя reduce метод:

list.reduce({}) { |hash, item| 
    (hash[item[:day]] ||= []) << item; hash 
} 

Вот другой, используя метод map, но вы должны нести переменный держатель вокруг:

hash = {} 
list.each { |item| 
    (hash[item[:day]] ||= []) << item 
} 

После того, как у вас есть несортированный хэш говорят в переменной foo, вы можете сортировать его как,

Hash[foo.sort] 
+0

Вы не должны использовать «карту», ​​если вы собираетесь отказаться от результата. Вместо этого используйте 'each'! –

+0

@ VictorMoroz - спасибо, что указали это. Я добавил изменения. – Anurag

0

Простой ответ:

data = [ 
    {:day=>4,:name=>'Jay'}, 
    {:day=>1,:name=>'Ben'}, 
    {:day=>4,:name=>'Jill'} 
] 

#expected solution 
sol = { 
    1=>[{:day=>1,:name=>'Ben'}], 
    4=>[{:day=>4,:name=>'Jay'},{:day=>4,:name=>'Jill'}] 
} 

res = {} 
data.each{|h| 
    res[h[:day]] ||= [] 
    res[h[:day]] << h 
} 

p res 
p res == sol #check value 
p res.keys == sol.keys #check order 

Проблема с этим решением: Хэш не отсортирован по запросу. (У той же проблемы Anurags solution).

Таким образом, вы должны изменить Ответ на этот вопрос немного:

res = {} 
data.sort_by{|h| h[:day]}.each{|h| 
    res[h[:day]] ||= [] 
    res[h[:day]] << h 
} 

p res 
p res == sol #check value 
p res.keys == sol.keys #check order 
0

В Rails можно использовать OrderedHash:

ActiveSupport::OrderedHash[arr.group_by { |a| a[:day] }.sort_by(&:first)] 

Update: На самом деле в Рубине 1,9 хэш упорядочен, поэтому использование ActiveSupport продление не требуется:

Hash[arr.group_by { |a| a[:day] }.sort_by(&:first)] 
Смежные вопросы