简体   繁体   中英

How to merge two arrays of hashes

I have two arrays of hashes:

a = [
  {
    key: 1,
    value: "foo"
  },
  {
    key: 2,
    value: "baz"
  }
]

b = [
  {
    key: 1,
    value: "bar"
  },
  {
    key: 1000,
    value: "something"
  }
]

I want to merge them into one array of hashes, so essentially a + b except I want any duplicated key in b to overwrite those in a . In this case, both a and b contain a key 1 and I want the final result to have b 's key value pair.

Here's the expected result:

expected = [
  {
    key: 1,
    value: "bar"
  },
  {
    key: 2,
    value: "baz"
  },
  {
    key: 1000,
    value: "something"
  }
]

I got it to work but I was wondering if there's a less wordy way of doing this:

hash_result = {}
a.each do |item|
  hash_result[item[:key]] = item[:value]
end

b.each do |item|
  hash_result[item[:key]] = item[:value]
end

result = []
hash_result.each do |k,v|
  result << {:key => k, :value => v}
end

puts result

puts expected == result # prints true

uniq would work if you concatenate the arrays in reverse order:

(b + a).uniq { |h| h[:key] }
#=> [
#     {:key=>1, :value=>"bar"},
#     {:key=>1000, :value=>"something"},
#     {:key=>2, :value=>"baz"}
#   ]

It doesn't however preserve the order.

[a, b].map { |arr| arr.group_by { |e| e[:key] } }
      .reduce(&:merge)
      .flat_map(&:last)

Here we use hash[:key] as a key to build the new hash, then we merge them overriding everything with the last value and return values .

I would rebuild your data a bit, since there are redundant keys in hashes:

thin_b = b.map { |h| [h[:key], h[:value]] }.to_h
#=> {1=>"bar", 1000=>"something"}
thin_a = b.map { |h| [h[:key], h[:value]] }.to_h
#=> {1=>"bar", 1000=>"something"}

Then you can use just Hash#merge :

thin_a.merge(thin_b)
#=> {1=>"bar", 2=>"baz", 1000=>"something"}

But, if you want, you can get exactly result as mentioned in question:

result.map { |k, v| { key: k, value: v } }
#=> [{:key=>1, :value=>"bar"}, 
#    {:key=>2, :value=>"baz"}, 
#    {:key=>1000, :value=>"something"}]

使用Enumerable#group_byEnumerable#map

(b+a).group_by { |e| e[:key] }.values.map {|arr| arr.first}

If you need to merge two arrays of hashes that should be merged also and there is more than two keys, then next snippet should help:

[a, b].flatten
      .compact
      .group_by { |v| v[:key] }
      .values
      .map { |e| e.reduce(&:merge) }

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