ruby - Merge two arrays of hashes based on comparison of multiple keys -


i have 2 arrays of hashes:

a1 = [{ id: 12496, name: "robert", email: "robert@hotmail.com" }, ...] a2 = [{ id: 12496, name: "robert", order_no: 5511426 }, ...] 

i find hashes in a2 id , name fields match id , name fields of entry in a1 (without caring email or other items make way a2), , merge value of order_no a1 hash @ values. i.e. end with:

[{ id: 12496, name: "robert", email: "robert@example.com", order_no: 5511426 } ...] 

also want ignore elements present in a2 not in a1.

i'm doing following:

a1.each |a1_hash|   matching_hash = a2.find { |a2_hash| data_matches?(a1_hash, a2_hash) } if a2.present?   a1_hash["order_no"] = a2_hash["order_no"] if matching_hash.present?   a2.delete(a2_hash) end 

but there faster way?

this can done quite cleanly using few of ruby's built-in methods.

a1 = [{ id: 12496, name: "robert", email: "robert@hotmail.com" },       { id: 12497, name: "lola",   email: "lola@hotmail.com" },       { id: 12498, name: "hank",   email: "hank@hotmail.com" }]  a2 = [{ id: 12497, name: "lola",   order_no: 5511427 },       { id: 12496, name: "robert", order_no: 5511426 }]  index = a2.group_by{|entry| [entry[:id], entry[:name]] } a1.map{|entry| (index[[entry[:id], entry[:name]]] || []).reduce(entry, :merge) } 

result:

[{:id=>12496, :name=>"robert", :email=>"robert@hotmail.com", :order_no=>5511426},  {:id=>12497, :name=>"lola",   :email=>"lola@hotmail.com",   :order_no=>5511427},  {:id=>12498, :name=>"hank",   :email=>"hank@hotmail.com"}] 

breakdown:

first, use group_by build table of entries in a2 potentially merged entries in a1. index table on id , name keys, since factors we're using determine entries match:

index = a2.group_by{|entry| [entry[:id], entry[:name]] } 

this produces result:

{[12497, "lola"]=>[{:id=>12497,   :name=>"lola",   :order_no=>5511427}],   [12496, "robert"]=>[{:id=>12496, :name=>"robert", :order_no=>5511426}]} 

next, map each entry in a1 new form, order numbers in index merged:

a1.map{|entry|   # ... } 

to value we're mapping each entry to, start getting array containing values in a2 suitable merge entry a1:

(index[[entry[:id], entry[:name]]] || []) 

this return [{:id=>12497, :name=>"lola", :order_no=>5511427}] lola, , empty array hank, has no matching entry in a2.

then, starting entry a1, reduce entries index 1 hash using merge (e.g. reduce(entry, :merge)), results in entry {:id=>12496, :name=>"robert", :email=>"robert@hotmail.com", :order_no=>5511426}.

all might seem bit complicated if you're unfamiliar methods in ruby's core library. once understand simple functional programing concepts map , reduce, it's not difficult come simple , powerful solutions this.


Comments

Popular posts from this blog

angularjs - ADAL JS Angular- WebAPI add a new role claim to the token -

php - CakePHP HttpSockets send array of paramms -

node.js - Using Node without global install -