2017-02-25 1 views
2

Ich versuche, die folgenden Arrays von Hashes LEFT JOIN:in Ruby: wie "links Join" auf zwei Arrays von Hashes auszuführen effizient

input: 
a = [{id: 1, name: 'Bob'}, {id: 2, name: 'Jack'}, {id: 3, name: 'Tom'}] 
b = [{id: 3, age: 12}, {id: 2, age: 7}] 
output: 
[{id: 1, name: 'Bob', age: nil}, {id: 2, name: 'Jack', age: 7}, {id: 3, name: 'Tom', age: 12}] 

Derzeit mache ich mit entlang der Linien etwas:

a.map do |x| 
    { 
    id: x[:id], 
    name: x[:name], 
    age: (b.detect{|y| x[:id] == y[:id]} || {age: nil}).fetch(:age) 
    } 
end 

Es funktioniert, aber es ist super langsam, wenn der Datensatz groß ist.

Gibt es eine bessere Möglichkeit, den Join-Vorgang effizienter durchzuführen?

+2

die linke Array von Hashes in einen Hash von Hashes auf dem 'verkeilt Turn: id' so Sie' haben { 1 => {id: 1, Name: 'Bob'}, 2 => {id: 2, Name: 'Jack'}, ...} '. – Ryan

+0

@CarySwoveland Sie haben Recht. Fest! – mye

Antwort

3
h = b.each_with_object({}) { |g,h| h[g[:id]] = g[:age] } 
    #=> {3=>12, 2=>7} 
a.map { |g| g.merge(age: h[g[:id]]) } 
    #=> [{:id=>1, :name=>"Bob", :age=>nil}, 
    # {:id=>2, :name=>"Jack", :age=>7}, 
    # {:id=>3, :name=>"Tom", :age=>12}] 

Wenn a an Ort und Stelle geändert werden, ändern Sie die zweite Zeile zu

a.each { |g| g[:age] = h[g[:id]] } 

a #=> [{:id=>1, :name=>"Bob", :age=>nil}, 
    # {:id=>2, :name=>"Jack", :age=>7}, 
    # {:id=>3, :name=>"Tom", :age=>12}] 
+0

Das ist definitiv rubiniger als meins; Da ich glücklicherweise Ruby vor ein paar Monaten verlassen habe, scheinen 90% der Sehkraft verloren zu sein. – mudasobwa

+0

@mudasobwa, bist du in einer anderen Sprache, hast deinen Beruf gewechselt oder bist aus der Belegschaft ausgeschieden? Löscht diesen Kommentar später. –

+0

Ich verliebte mich in [Elixir] (http://elixir-lang.org). Ich hatte einmal einen Beruf gewechselt, um zu schreiben (neben Universitätszeit und Berufserfahrung), und ich bin ziemlich sicher, dass ich für immer programmieren werde. – mudasobwa

4
[a, b].map { |a| a.group_by { |e| e[:id] } } 
     .reduce do |a, b| 
      a.merge(b) { |_, v1, v2| v1.first.merge v2.first } 
     end.values 
     .map do |e| 
     Array === e ? {age:nil, name:nil}.merge(e.first) : e 
     end 

Der gesamte Vorbereitungsschritt nimmt O(N) und die Zusammenführung wird dann als O(N) getan, plus die Finalisierung O(N).

Verwandte Themen