2016-09-08 1 views
0

Hinweis: Es gab ein paar ähnlichen Fragen auf SO darüber, wie here und here, aber keiner scheint ganz wie das, was ich suche.Wie "reduziert" Array von Hashes mit doppelten Schlüsseln zu verschachtelten Hash?

sagen, dass ich ein Array von Hashes wie dieses:

arr_with_dup_hsh_keys = [ 
    { foo: "dup", bar: 1 }, 
    { foo: "dup", bar: 2 }, 
    { foo: "dup", bar: 3 }, 
    { foo: "dup", bar: 4 }, 
    { foo: "dup", bar: 5 } 
] 

Wie reduziere ich das diese nach unten?

{ foo: "dup", bars: [1, 2, 3, 4, 5] } 

Und was, wenn es für foo unterschiedliche Werte sind?

arr_with_dup_hsh_keys = [ 
    { foo: "dup", bar: 1 }, 
    { foo: "dup", bar: 2 }, 
    { foo: "soup", bar: 3 }, 
    { foo: "dup", bar: 4 }, 
    { foo: "soup", bar: 5 } 
] 
+0

Haben Sie versucht, etwas? –

+0

Ja, ich habe mehrere Versuche mit 'group_by' und' inject' ausprobiert, aber nicht annähernd in der Nähe dessen, was ich erreichen möchte. – binarymason

+3

'{foo:" dup ", Balken: arr_with_dup_hsh_keys.map {| hsh | hsh [: bar]}} '. –

Antwort

2
def combine(arr) 
    arr.group_by {|g|g[:foo]}.values.map {|a|{foo: a.first[:foo], bar: a.map {|g| g[:bar]}}} 
end 

combine arr_with_dup_hsh_keys 
    #=> [{:foo=>"dup", :bar=>[1, 2, 3, 4, 5]}] 

arr_with_dup_hsh_keys1 = [ 
    { foo: "dup", bar: 1 }, 
    { foo: "dup", bar: 2 }, 
    { foo: "soup", bar: 3 }, 
    { foo: "dup", bar: 4 }, 
    { foo: "soup", bar: 5 } 
] 

combine arr_with_dup_hsh_keys1 
    #=> [{:foo=>"dup", :bar=>[1, 2, 4]}, {:foo=>"soup", :bar=>[3, 5]}] 

Enumerable#group_by See und beachten Sie, dass

arr_with_dup_hsh_keys1.group_by { |g| g[:foo] } 
#=> {"dup"=> [{:foo=>"dup", :bar=>1}, {:foo=>"dup", :bar=>2}, 
#    {:foo=>"dup", :bar=>4}], 
# "soup"=>[{:foo=>"soup", :bar=>3}, {:foo=>"soup", :bar=>5}]} 

Sie könnten alternativ schreibe folgendes.

def combine(arr) 
    arr.each_with_object({}) do |g,h| 
    f = g.merge(bar: [g[:bar]]) 
    h.update(f[:foo]=>f) { |_,o,n| { foo: o[:foo], bar: o[:bar]+n[:bar] } } 
    end.values 
end 

combine arr_with_dup_hsh_keys1 
    #=> [{:foo=>"dup", :bar=>[1, 2, 4]}, {:foo=>"soup", :bar=>[3, 5]}] 

Dies nutzt die Form Hash#update (aka merge!), die einen Block die Werte des Schlüssels zu bestimmen, verwendet, die in beiden fusionierten Hashes vorhanden sind. Eine Erläuterung der drei Blockvariablen finden Sie im Dokument (der erste ist der allgemeine Schlüssel, den ich mit einem Unterstrich dargestellt habe, um anzuzeigen, dass er nicht in der Blockberechnung verwendet wird).

+0

genau das habe ich gebraucht. Sie müssen gespürt haben, dass ich auch unterscheiden musste, wenn 'foo'' bar' oder 'soup' sein konnte. Vielen Dank! – binarymason

+0

Es wird manchmal ein "mindmeld" genannt. –

+0

Ich begann zunächst mit 'group_by', hatte aber Probleme, weil dadurch eine weitere Array-Ebene erstellt wurde. Wie würden Sie die Methode mit 'group_by' fortsetzen? (Wenn Sie nichts dagegen haben) – binarymason

1

Wenn Ihre Daten in Ihrer Frage wirklich so einfach ist, wird dies tun, was Sie wollen:

{ foo: "dup", 
    bars: arr_with_dup_hsh_keys.map {|hsh| hsh[:bar] } 
} 
+0

Daten sind wirklich nicht so einfach. Ich habe vielleicht Hunderte von 'foo's, die eine Reihe von Werten haben und viele von ihnen sind Duplikate wie das obige Beispiel. – binarymason

1

Das ist, was ich kam:

a = [ 
    { foo: "dup", bar: 1 }, 
    { foo: "dup", bar: 2 }, 
    { foo: "dup", bar: 3 }, 
    { foo: "dup", bar: 4 }, 
    { foo: "dup", bar: 5 } 
] 

h = {} 
a.map(&:keys).uniq.flatten.each_with_index do |key, idx| 
    h[key] = a.map(&:values).collect { |a| a[idx]}.uniq 
end 
h 
#=> {:foo=>["dup"], :bar=>[1, 2, 3, 4, 5]} 
Verwandte Themen