2015-01-29 6 views
1

Я стараюсь изо всех сил манипулировать данными в рубине.groupby массив хэшей в массив хэшей массивов?

Учитывая этот массив хэшей

peepz = [ 
{ 
    :name => 'ted', 
    :phone => '555-1234', 
    :color => 'red' 
}, 
{ 
    :name => 'bill', 
    :phone => '555-2222', 
    :color => 'green' 
}, 
{ 
    :name => 'ted', 
    :phone => '555-3333', 
    :color => 'orange' 
}, 
{ 
    :name => 'dan', 
    :phone => '555-7777', 
    :color => 'violet' 
}, 
{ 
    :name => 'ted', 
    :phone => '555-4444', 
    :color => 'yellow' 
}, 
{ 
    :name => 'dan', 
    :phone => '555-6666', 
    :color => 'orange' 
} 
]; 

я пожелаю этот выход

peepz2 = [ 
    { 
    :name => 'ted' 
    :phone => ['555-1212','555-3333','555-4444'] 
    :color => ['red','orange','yellow'] 
    }, 
    { 
    :name => 'bill' 
    :phone => ['555-2222'] 
    :color => ['green'] 
    }, 
    { 
    :name => 'dan' 
    :phone => ['555-7777','555-6666'] 
    :color => ['violet','orange'] 
    }, 
] 

вот что я до сих пор.

def groupbyer (peepz, *fields) 
    peepz.groupby do |peep| 
     key = '' 
     fields.each do |field| 
     key += peep[field].to_s 
     end 
    end.each do |group| 
     ???? 
    end 
end 

groupbyer(peepz, :name) 

Этот метод должен быть обобщен. он не может жестко закодировать поля peepz

Какой хороший рубиновый способ закончить это?

ответ

4
peepz 
.group_by{|h| h[:name]}.values 
.map{|a| a.inject{|h1, h2| h1.merge(h2){|k, v1, v2| k == :name ? v1 : [*v1, v2]}}} 
1

Другой способ использует форму Hash#update (ака merge!), который использует блок для определения значений ключей, присутствующих в обоих хешей объединяемых:

код

def peep_merge(peepz) 
    peepz.each_with_object({}) { |g,h| 
     h.update(g[:name]=>(g.merge(g) { |*_,v| 
      [v] })) { |_,oh,nh| oh.merge(nh) { |_,ov,nv| ov+nv } } } 
     .values 
     .each { |h| h[:name] = h[:name].first } 
end 

Пример

Длядано в вопросе:

peep_merge(peepz) 
    #=> [{:name=>"ted",     
    #  :phone=>["555-1234", "555-3333", "555-4444"], 
    #  :color=>["red", "orange", "yellow"]}, 
    # {:name=>"bill", 
    #  :phone=>["555-2222"], 
    #  :color=>["green"]}, 
    # {:name=>"dan", 
    #  :phone=>["555-7777", "555-6666"], 
    #  :color=>["violet", "orange"]}] 

Объяснение

a = peepz.each_with_object({}) { |g,h| 
     h.update(g[:name]=>(g.merge(g) { |*_,v| 
     [v] })) { |_,oh,nh| oh.merge(nh) { |_,ov,nv| ov+nv } } } 
    #=> {"ted" =>{:name=>["ted", "ted", "ted"], 
    #    :phone=>["555-1234", "555-3333", "555-4444"], 
    #    :color=>["red", "orange", "yellow"]}, 
    # "bill"=>{:name=>["bill"], 
    #    :phone=>["555-2222"], 
    #    :color=>["green"]}, 
    # "dan" =>{:name=>["dan", "dan"], 
    #    :phone=>["555-7777", "555-6666"], 
    #    :color=>["violet", "orange"]}} 
b = a.values 
    #=>   [{:name =>["ted", "ted", "ted"], 
    #    :phone=>["555-1234", "555-3333", "555-4444"], 
    #    :color=>["red", "orange", "yellow"]}, 
    #   {:name =>["bill"], 
    #    :phone=>["555-2222"], 
    #    :color=>["green"]}, 
    #   {:name =>["dan", "dan"], 
    #    :phone=>["555-7777", "555-6666"], 
    #    :color=>["violet", "orange"]}] 

Наконец, чтобы исправить значения для :name ключей:

b.each { |h| h[:name] = h[:name].first } 

обеспечивает результат, показанный в приведенном выше примере.

Смежные вопросы