Ruby: разделить один хэш на два в зависимости от желаемых ключей

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

Если у меня есть следующие две переменные:

hash = {:a => 1, :b => 2, :c => 3, :d => 4}
keyset = [:a, :c]

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

hash1 = {:a => 1, :c => 3}
hash2 = {:b => 3, :d => 4}

Если пример не проясняет мою цель, по сути, я хочу гибрид между #delete и #delete_if - #delete возвращает удаленное значение, тогда как #delete_if позволяет мне удалять массово. Я бы предпочел способ массового удаления и возврата удаленных значений - или что-то эквивалентное.

Спасибо!


person Matchu    schedule 10.07.2009    source источник


Ответы (5)


Попробуйте Active Support с Hash#slice и/или Hash#except. Методы взрыва также существуют:

$ irb
>> require 'active_support/core_ext'
=> true

>> hash = {:a => 1, :b => 2, :c => 3, :d => 4}
=> {:a=>1, :d=>4, :b=>2, :c=>3}
>> keyset = [:a, :c]
=> [:a, :c]

>> remainders = hash.slice!(*keyset)
=> {:d=>4, :b=>2}

>> remainders
=> {:d=>4, :b=>2}
>> hash
=> {:a=>1, :c=>3}
person Ryan McGeary    schedule 10.07.2009

hash = { a: 1, b: 2, c: 3, d: 4 }
keyset = [:a, :c]

left, right = hash.partition {|k,v| keyset.include? k }

Это оставляет левое и правое как массивы массивов; превратить обратно в хеш:

left = Hash[left]
right = Hash[right]

puts "left=#{left.inspect}"
puts "right=#{right.inspect}"
person Kim Toms    schedule 09.03.2013
comment
Похоже на недостаток дизайна Ruby, что .partition сам по себе не является ответом; учитывая, что Hash#select и Hash#reject возвращают хэши, для Hash#partition имеет смысл возвращать массив из двух хэшей. Но, возможно, я что-то упускаю. - person Nathan Long; 23.10.2015
comment
Если вы хотите вернуть хэш (в Ruby 2.0+), вы можете сделать left, right = hash.partition{ |k, v| keyset.include?(k) }.map(&:to_h) - person pnomolos; 03.05.2016

new_hash = {}
keyset.each {|i| new_hash[i] = hash.delete(i)}

Это, казалось, сделало это для меня, без дополнительных требований

person Mark W    schedule 10.07.2009
comment
Я не упомянул, что был на Rails — так как это казалось неуместным — но это значит, что у меня уже есть Active Support. Но все равно спасибо! :) - person Matchu; 10.07.2009

С поддержкой Rails/Active вы можете использовать метод extract!:

hash = {:a => 1, :b => 2, :c => 3, :d => 4}
keyset = [:a, :c]

hash2 = hash.extract! *keyset
>> {:a=>1, :c=>3}

hash
>> {:b=>2, :d=>4}
person D-Rock    schedule 07.06.2016

Если вы не возражаете против внешних зависимостей, вы можете использовать https://github.com/renra/split-off-ruby Затем вы можете сделать:

hash2 = hash.split_off!(:b, :d)

hash по-прежнему будет содержать исходные значения ключей :a и :c. Вышеупомянутые методы достаточно хороши, но я считаю, что иногда лучше выразить свои намерения с помощью правильного имени метода.

person Renra    schedule 10.01.2014