Как объединить два хэша без новых ключей - PullRequest
14 голосов
/ 28 января 2011

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

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

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

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

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

Ответы [ 5 ]

14 голосов
/ 28 января 2011

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

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

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

8 голосов
/ 20 мая 2011

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

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

Первая загрузка активной поддержки:

require 'active_support/core_ext'

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

h.merge(j.slice(*h.keys))
* * Пример тысяча двадцать-один: * * 1 022
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)

Бонус:

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

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)

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

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

8 голосов
/ 28 января 2011

Ответ Йерема работает в 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 в вашем хэше, вы должны сделать это:

h.reject { |k,v| !j.has_key? k or j[k] != h[k] }
0 голосов
/ 15 декабря 2014

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

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

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


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

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

  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"}
0 голосов
/ 28 января 2011
[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
...