Как объединить два массива хэшей

У меня есть два массива хэшей:

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

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

Я хочу объединить их в один массив хэшей, так что по сути a + b кроме того, я хочу любой дублированный ключ в b переписать те в a, В этом случае оба a а также b содержать ключ 1 и я хочу, чтобы конечный результат bпара ключ-значение.

Вот ожидаемый результат:

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

Я получил его на работу, но мне было интересно, если есть менее многословный способ сделать это:

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

4 ответа

Решение

uniq будет работать, если вы объедините массивы в обратном порядке:

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

Это, однако, не сохраняет порядок.

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

Здесь мы используем hash[:key] в качестве ключа для создания нового хэша, то мы merge они перекрывают все с последним значением и возвращают values,

Я бы немного перестроил ваши данные, так как в хешах есть избыточные ключи:

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"}

Тогда вы можете использовать только Hash#merge:

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

Но, если вы хотите, вы можете получить именно такой результат, как указано в вопросе:

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

Используя Enumerable#group_by и Enumerable#map

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

Если вам нужно объединить два массива хэшей, которые также должны быть объединены, и имеется более двух ключей, то следующий фрагмент должен помочь:

[a, b].flatten
      .compact
      .group_by { |v| v[:key] }
      .values
      .map { |e| e.reduce(&:merge) }
Другие вопросы по тегам