Как удалить ключ из Hash и получить оставшийся хэш в Ruby/Rails?
Чтобы добавить новую пару к Hash, я делаю:
{:a => 1, :b => 2}.merge!({:c => 3}) #=> {:a => 1, :b => 2, :c => 3}
Есть ли аналогичный способ удаления ключа из Hash?
Это работает:
{:a => 1, :b => 2}.reject! { |k| k == :a } #=> {:b => 2}
но я ожидал бы что-то вроде:
{:a => 1, :b => 2}.delete!(:a) #=> {:b => 2}
Важно, что возвращаемое значение будет оставшимся хешем, поэтому я мог бы делать такие вещи, как:
foo(my_hash.reject! { |k| k == my_key })
в одной строке.
Ответы
Ответ 1
Rails имеет исключение/исключение! метод, который возвращает хэш с удаленными ключами. Если вы уже используете Rails, нет смысла создавать собственную версию этого.
class Hash
# Returns a hash that includes everything but the given keys.
# hash = { a: true, b: false, c: nil}
# hash.except(:c) # => { a: true, b: false}
# hash # => { a: true, b: false, c: nil}
#
# This is useful for limiting a set of parameters to everything but a few known toggles:
# @person.update(params[:person].except(:admin))
def except(*keys)
dup.except!(*keys)
end
# Replaces the hash without the given keys.
# hash = { a: true, b: false, c: nil}
# hash.except!(:c) # => { a: true, b: false}
# hash # => { a: true, b: false }
def except!(*keys)
keys.each { |key| delete(key) }
self
end
end
Ответ 2
Oneliner plain ruby, он работает только с ruby > 1.9.x:
1.9.3p0 :002 > h = {:a => 1, :b => 2}
=> {:a=>1, :b=>2}
1.9.3p0 :003 > h.tap { |hs| hs.delete(:a) }
=> {:b=>2}
Tap метод всегда возвращает объект, на который вызывается...
В противном случае, если вам требуется active_support/core_ext/hash
(что автоматически требуется в каждом приложении Rails), вы можете использовать один из следующих способов в зависимости от ваших потребностей:
➜ ~ irb
1.9.3p125 :001 > require 'active_support/core_ext/hash' => true
1.9.3p125 :002 > h = {:a => 1, :b => 2, :c => 3}
=> {:a=>1, :b=>2, :c=>3}
1.9.3p125 :003 > h.except(:a)
=> {:b=>2, :c=>3}
1.9.3p125 :004 > h.slice(:a)
=> {:a=>1}
except использует черный список, поэтому он удаляет все ключи, перечисленные в качестве аргументов, а slice использует белый список, поэтому он удаляет все ключи, которые не указаны в качестве аргументов. Также существует версия bang этого метода (except!
и slice!
), которые изменяют данный хеш, но их возвращаемое значение отличается, оба из них возвращают хэш. Он отображает удаленные ключи для slice!
и ключи, которые хранятся для except!
:
1.9.3p125 :011 > {:a => 1, :b => 2, :c => 3}.except!(:a)
=> {:b=>2, :c=>3}
1.9.3p125 :012 > {:a => 1, :b => 2, :c => 3}.slice!(:a)
=> {:b=>2, :c=>3}
Ответ 3
Почему бы просто не использовать:
hash.delete(key)
Ответ 4
Есть много способов удалить ключ из хеша и получить оставшийся хеш в Ruby.
-
.slice
=> Он вернет выбранные ключи и не удалит их из исходного хэша. Используйте slice!
если вы хотите удалить ключи навсегда, используйте простой slice
.
2.2.2 :074 > hash = {"one"=>1, "two"=>2, "three"=>3}
=> {"one"=>1, "two"=>2, "three"=>3}
2.2.2 :075 > hash.slice("one","two")
=> {"one"=>1, "two"=>2}
2.2.2 :076 > hash
=> {"one"=>1, "two"=>2, "three"=>3}
-
.delete
=> Он удалит выбранные ключи из исходного хэша (он может принять только один ключ и не более одного).
2.2.2 :094 > hash = {"one"=>1, "two"=>2, "three"=>3}
=> {"one"=>1, "two"=>2, "three"=>3}
2.2.2 :095 > hash.delete("one")
=> 1
2.2.2 :096 > hash
=> {"two"=>2, "three"=>3}
-
.except
=> Он вернет оставшиеся ключи, но не удалит ничего из исходного хеша. Используйте except!
если вы хотите удалить ключи навсегда, используйте простой, except
.
2.2.2 :097 > hash = {"one"=>1, "two"=>2, "three"=>3}
=> {"one"=>1, "two"=>2, "three"=>3}
2.2.2 :098 > hash.except("one","two")
=> {"three"=>3}
2.2.2 :099 > hash
=> {"one"=>1, "two"=>2, "three"=>3}
-
.delete_if
=> В случае, если вам нужно удалить ключ на основе значения. Очевидно, он удалит соответствующие ключи из исходного хэша.
2.2.2 :115 > hash = {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1}
=> {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1}
2.2.2 :116 > value = 1
=> 1
2.2.2 :117 > hash.delete_if { |k,v| v == value }
=> {"two"=>2, "three"=>3}
2.2.2 :118 > hash
=> {"two"=>2, "three"=>3}
-
.compact
=> Используется для удаления всех значений nil
из хеша. Используйте compact!
если вы хотите удалить значения nil
навсегда, используйте простой compact
.
2.2.2 :119 > hash = {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil}
=> {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil}
2.2.2 :120 > hash.compact
=> {"one"=>1, "two"=>2, "three"=>3}
Результаты на основе Ruby 2.2.2.
Ответ 5
Если вы хотите использовать чистый Ruby (no Rails), не хотите создавать методы расширения (возможно, вам нужно это только в одном или двух местах и не хотите загрязнять пространство имен с помощью множества методов) t хотите отредактировать хеш на месте (т.е. вы поклонник функционального программирования, такого как я), вы можете "выбрать":
>> x = {:a => 1, :b => 2, :c => 3}
=> {:a=>1, :b=>2, :c=>3}
>> x.select{|x| x != :a}
=> {:b=>2, :c=>3}
>> x.select{|x| ![:a, :b].include?(x)}
=> {:c=>3}
>> x
=> {:a=>1, :b=>2, :c=>3}
Ответ 6
#in lib/core_extensions.rb
class Hash
#pass single or array of keys, which will be removed, returning the remaining hash
def remove!(*keys)
keys.each{|key| self.delete(key) }
self
end
#non-destructive version
def remove(*keys)
self.dup.remove!(*keys)
end
end
#in config/initializers/app_environment.rb (or anywhere in config/initializers)
require 'core_extensions'
Я установил это так, чтобы .remove вернул копию хэша с удаленными ключами, а удаляем! изменяет сам хеш. Это соответствует рубиновым соглашениям. например, с консоли
>> hash = {:a => 1, :b => 2}
=> {:b=>2, :a=>1}
>> hash.remove(:a)
=> {:b=>2}
>> hash
=> {:b=>2, :a=>1}
>> hash.remove!(:a)
=> {:b=>2}
>> hash
=> {:b=>2}
>> hash.remove!(:a, :b)
=> {}
Ответ 7
Вы можете использовать except!
из драгоценного камня facets
:
>> require 'facets' # or require 'facets/hash/except'
=> true
>> {:a => 1, :b => 2}.except(:a)
=> {:b=>2}
Исходный хеш не изменяется.
EDIT: как говорит Рассел, грани имеют некоторые скрытые проблемы и не полностью совместимы с API с ActiveSupport. С другой стороны ActiveSupport не так полно, как грани. В конце концов, я бы использовал AS и допустил ошибки в вашем коде.
Ответ 8
Вместо исправления обезьяны или без необходимости включения больших библиотек вы можете использовать уточнения, если вы используете Ruby 2:
module HashExtensions
refine Hash do
def except!(*candidates)
candidates.each { |candidate| delete(candidate) }
self
end
def except(*candidates)
dup.remove!(candidates)
end
end
end
Вы можете использовать эту функцию, не затрагивая другие части вашей программы, или иметь большие внешние библиотеки.
class FabulousCode
using HashExtensions
def incredible_stuff
delightful_hash.except(:not_fabulous_key)
end
end
Ответ 9
в чистом Ruby:
{:a => 1, :b => 2}.tap{|x| x.delete(:a)} # => {:b=>2}
Ответ 10
См. Ruby on Rails: удаление нескольких хеш-ключей
hash.delete_if{ |k,| keys_to_delete.include? k }
Ответ 11
Было бы здорово, если delete вернул пару удаления хэша. Я делаю это:
hash = {a: 1, b: 2, c: 3}
{b: hash.delete(:b)} # => {:b=>2}
hash # => {:a=>1, :c=>3}
Ответ 12
Это однострочный способ сделать это, но он не очень читабельен. Вместо этого рекомендуется использовать две строки.
use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call)
Ответ 13
Несколько способов удалить Key in Hash. Вы можете использовать любой метод снизу
hash = {a: 1, b: 2, c: 3}
hash.except!(:a) # Will remove *a* and return HASH
hash # Output :- {b: 2, c: 3}
hash = {a: 1, b: 2, c: 3}
hash.delete(:a) # will remove *a* and return 1 if *a* not present than return nil
Есть много способов, вы можете посмотреть на Ruby doc of Hash здесь.
Спасибо
Ответ 14
Это также сработает: hash[hey] = nil