Подтвердить что ты не робот

Как объединить два хэша без новых ключей

Как я могу объединить два хэша, которые не приводят к новым ключам, что означает, что слияние будет объединять ключи, существующие в обеих хэшах?

Например, я хочу следующее:

h = {:foo => "bar"}
j = {:foo => "baz", :extra => "value"}

puts h.merge(j)    # {:foo => "baz"}

Я ищу действительно чистый способ сделать это, так как моя текущая реализация довольно грязная.

4b9b3361

Ответ 1

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

h.merge j.select { |k| h.keys.include? k }

В отличие от моей измененной альтернативы, это безопасно, если вы решите изменить ее на merge! или update.

Ответ 2

Если вы используете activesupport (часть рельсов), вы можете использовать 2 дополнительных метода на Hash:

  • Hash#slice принимает желаемые ключи как отдельные аргументы (а не массив ключей) и возвращает новый хэш только с запрошенными вами ключами.
  • Hash#except принимает те же аргументы, что и slice, но возвращает новый хеш с ключами, которые не были в аргументах.

Первая загрузка активных активов:

require 'active_support/core_ext'

Объединить только записи из j, ключи которых уже находятся в h (т.е. изменить, но не добавлять или удалять записи в h):

h.merge(j.slice(*h.keys))

Пример:

ignore_new = ->(h, j) { h.merge(j.slice(* h.keys)) }
ignore_new.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12})
# => {:a=>1, :b=>10, :c=>11}

Получите остатки от j, которые не были в h:

j.except(*h.keys)

Bonus:

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

h.merge(j).slice(* ( h.keys & j.keys ) )

Пример:

intersect = ->(h, j) { h.merge(j).slice(* (h.keys & j.keys) ) }
intersect.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12})
# => {:b=>10, :c=>11}

и остатки от h, которые не были в j:

h.except(*j.keys)

Вы также можете использовать activesupport HashWithIndifferentAccess, если хотите, чтобы строка и ключ-доступ к символу считались эквивалентными.

Обратите внимание, что ни один из приведенных выше примеров не изменяет исходные хэши; вместо этого возвращаются новые хеши.

Ответ 3

Ответ Yjerem работает в Ruby 1.9, но не в 1.8.x. В 1.8.x метод Hash#select возвращает массив. Hash#reject возвращает хэш.

h.reject { |k,v| !j.keys.include? k }

Если вы хотите сохранить только пары ключ-значение, которые имеют одинаковые значения, вы можете сделать это:

h.reject { |k,v| j[k] != h[k] }

В краевом случае есть ниль. Если вы храните nils в своем Hash, вы должны это сделать:

h.reject { |k,v| !j.has_key? k or j[k] != h[k] }

Ответ 4

[h].inject({}) { |m,e| e.merge(j) { |k,o,n| m[k] = n }; m}

или

[{}].inject(h) { |m,e| m.merge(j) { |k,o,n| e[k] = n }; e}

или (возможно, лучшее, но не технически одно выражение)...

t = {}; h.merge(j) { |k,o,n| t[k] = n }; t

Ответ 5

Более индивидуальный способ сделать это:

 h = {"foo"=> "bar"}

 j = {"foo" => "baz", "extra" => "value"}


 k = h.merge(j)
result:  {"foo"=>"baz", "extra"=>"value"}

Здесь ключ "foo" во втором хэше переопределяет "foo" в первом хеше. Но если вы хотите сохранить старое значение i.e bar или хотите сохранить новое значение i.e "baz"? Вы можете сделать что-то вроде этого:

  k = h.merge(j){|key, old, new| old}
 result: {"foo"=>"bar", "extra"=>"value"}


k = h.merge(j){|key, old, new| new}

result: {"foo"=>"baz", "extra"=>"value"}