2013-05-16 2 views
2

Локальная переменная с именем журнала содержит массив хэшей с отметками времени событий, как так:Collapse журнал по дате в массив хэшей

log = [ 
    {time: 201201, x: 2}, 
    {time: 201201, y: 7}, 
    {time: 201201, z: 2}, 
    {time: 201202, a: 3}, 
    {time: 201202, b: 4}, 
    {time: 201202, c: 0} 
] 

мне нужно свернуть журнал по дате в массив хэшей, содержащий одна запись в день:

result = [ 
    {time: 201201, x: 2, y: 7, z: 2}, 
    {time: 201202, a: 3, b: 4, c: 0}, 
] 

ответ

5
log.group_by {|e| e[:time] }.map {|_,v| v.reduce(:merge) } 
+0

Вам не нужно '&'. – sawa

+0

@sawa действительно достаточно, хотя это специальный случай для уменьшения/инъекции. – dbenhur

+0

Вау! Это то, что я пытался сделать. Brilliant! –

0

Я решил проблему, но я не доволен этим решением. Кажется полезным метод group_by для такого рода проблем.

log.inject([]) do |acc, elem| 
    time = elem[:time] 
    found_by_time = acc.select {|hash| hash.any? {|i| i.include?(time)}} 
    found_by_time.empty? ? acc << elem : found_by_time.first.merge!(elem) 
    acc 
end 
2

насчет

log.each_with_object({}) do |e, a| 
    (a[e[:time]] ||= {}).merge!(e) 
end.values 
+0

Это лучшее решение, чем принятое. Но это выглядит не так красиво, как другое. Hash.new {| h, k | h [k] = {}} может немного помочь ... –

0
log = [ 
    {time: 201201, x: 2}, 
    {time: 201201, y: 7}, 
    {time: 201201, z: 2}, 
    {time: 201202, a: 3}, 
    {time: 201202, b: 4}, 
    {time: 201202, c: 0} 
] 

answer = log.reduce([]) do |result, value| 
    d = result.select {|h| h.any? {|i| i.include?(value[:time])}} 
    if d.empty? 
    result << value 
    else 
    d.first.merge!(value) 
    end 
    result 
end 

#=>{:time=>201201, :x=>2, :y=>7, :z=>2} 
#=>{:time=>201202, :a=>3, :b=>4, :c=>0}