简体   繁体   中英

How do I merge two arrays of hashes based on same hash key value?

So I have two arrays of hashes:

a = [{"b"=>123,"c"=>456}, {"b"=>456,"c"=>555}]
b = [{"c"=>456,"d"=>789},  {"b"=>222,"c"=>444}]

How would I concatenate them with the condition that the value of the key c is equivalent in both a and b ? Meaning I want to be able to concatenate with the condition of a['c'] == b['c']

This is the result I want to get:

final_array = [{"b"=>123,"c"=>456,"d"=>789}, {"b"=>456,"c"=>555}, {"b"=>222,"c"=>444}]
a = [{"b"=>123,"c"=>456}, {"b"=>456,"c"=>555}]
b = [{"c"=>456,"d"=>789}, {"b"=>222,"c"=>444}]

p a.zip(b).map{|h1,h2| h1["c"] == h2["c"] ? h1.merge(h2) : [h1 ,h2]}.flatten
# => [{"b"=>123, "c"=>456, "d"=>789}, {"b"=>456, "c"=>555}, {"b"=>222, "c"=>444}]
a = [{"b"=>123,"c"=>456}, {"b"=>456,"c"=>555}]
b = [{"c"=>456,"d"=>789},  {"b"=>222,"c"=>444}]

def merge_hashes_with_equal_values(array_of_hashes, key)
  array_of_hashes.sort { |a,b| a[key] <=> b[key] }.
    chunk { |h| h[key] }.
    each_with_object([]) { |h, result|  result << h.last.inject(&:merge) }
end

p merge_hashes_with_equal_values(a + b, 'c')

# => [{"b"=>222, "c"=>444}, {"c"=>456, "d"=>789, "b"=>123}, {"b"=>456, "c"=>555}]

Concatenate the arrays first, and pass it to the method with the hash key to combine on. Sorting that array then places the hashes to merge next to each other in another array, which makes merging a bit easier to program for. Here I chose #chunk to handle detection of continuous runs of hashes with equal keys to merge, and #each_with_object to compile the final array.

Since this method takes one array to work on, the length of the starting arrays does not need to be equal, and the ordering of those arrays does not matter. A downside is that the keys to operate on must contain a sortable value (no nils, for example).

Here is yet another approach to the problem, this one using a hash to build the result:

def merge_hashes_with_equal_values(array_of_hashes, key)
  result = Hash.new { |h,k| h[k] = {} }
  remainder = []
  array_of_hashes.each_with_object(result) do |h, answer|
    if h.has_key?(key)
      answer[h.fetch(key)].merge!(h)
    else
      remainder << h
    end
  end.values + remainder
end

Enumerable#flat_map and Hash#update are the perfect methods for this purpose :

a = [{"b"=>123,"c"=>456}, {"b"=>456,"c"=>555}]    
b = [{"c"=>456,"d"=>789}, {"b"=>222,"c"=>444}]
p a.zip(b).flat_map{|k,v| next k.update(v) if k["c"] == v["c"];[k,v]}
# >> [{"b"=>123, "c"=>456, "d"=>789}, {"b"=>456, "c"=>555}, {"b"=>222, "c"=>444}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM