Как удалить ключ из 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 })
в одну строку.
18 ответов
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
Простой рубин, он работает только с 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}
кроме того, использует подход черного списка, поэтому он удаляет все ключи, перечисленные как аргументы, в то время как slice использует подход белого списка, поэтому он удаляет все ключи, которые не перечислены в качестве аргументов. Существует также версия взрыва этого метода (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}
Есть много способов удалить ключ из хэша и получить оставшийся хэш в Ruby.
.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
=> Он вернет оставшиеся ключи, но не удалит ничего из исходного хеша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}
Результаты на основе Ruby 2.2.2.
Если вы хотите использовать чистый Ruby (без Rails), не хотите создавать методы расширения (возможно, вам это нужно только в одном или двух местах, и вы не хотите загрязнять пространство имен множеством методов) и не хотите отредактируйте хеш (т. е. вы любите функциональное программирование, как я), вы можете "выбрать":
>> 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}
Hash#except (Ruby 3.0+)
Начиная с Ruby 3.0, Hash#except является встроенным методом.
В результате больше не нужно полагаться на ActiveSupport или писать обезьяны-патчи, чтобы использовать его.
h = { a: 1, b: 2, c: 3 }
p h.except(:a) #=> {:b=>2, :c=>3}
Источники:
- Hash# кроме официальных документов Ruby.
- Ссылка на PR.
- Ruby 3.0 добавляет Hash # except и ENV.except.
#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)
=> {}
Ты можешь использовать except!
от facets
перл:
>> require 'facets' # or require 'facets/hash/except'
=> true
>> {:a => 1, :b => 2}.except(:a)
=> {:b=>2}
Оригинальный хеш не меняется.
РЕДАКТИРОВАТЬ: как говорит Рассел, фасеты имеют некоторые скрытые проблемы и не полностью API-совместимы с ActiveSupport. С другой стороны ActiveSupport не так полон, как аспекты. В конце концов, я бы использовал AS и предоставил крайние случаи в вашем коде.
Вместо того, чтобы вносить исправления обезьяны или без необходимости включать большие библиотеки, вы можете использовать уточнения, если вы используете 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
В чистом рубине
{:a => 1, :b => 2}.tap{|x| x.delete(:a)} # => {:b=>2}
См. Ruby on Rails: Удалить несколько ключей хеша
hash.delete_if{ |k,| keys_to_delete.include? k }
Было бы здорово, если delete вернул удаленную пару хешей. Я делаю это:
hash = {a: 1, b: 2, c: 3}
{b: hash.delete(:b)} # => {:b=>2}
hash # => {:a=>1, :c=>3}
Попробуйте
except!
метод.
{:a => 1, :b => 2}.except!(:a) #=> {:b => 2}
Использоватьdelete
,except
, или
sample_hash = {hey: 'hey', hello: 'hello'}
Удалить:
sample_hash.delete(:hey)
=> 'hey'
sample_hash
=> {hello: 'hello'}
Возвращает значение ключа и удаляет ключ в исходном объекте, возвращает ноль, если такого ключа нет.
Кроме:
sample_hash.except(:hey)
=> {hello: 'hello'}
sample_hash
=> {hey: 'hey', hello: 'hello'}
Возвращает весь хэш без указанных ключей, но не обновляет исходный хеш.
Кроме!:except!
то же самое, что и исключение, но оно постоянно меняет состояние исходного хэша, как это делают все методы, работающие с ударом.
sample_hash.except!(:hey)
=> {hello: 'hello'}
sample_hash
=> {hello: 'hello'}
Я хочу удалить список ключей и вернуть удаленный «фрагмент» хеша:
Рельсы:
hash = {a: 1, b: 2, c: 3}
def delete_slice!(hash, *keys)
hash.slice(*keys).tap { hash.except!(*keys) }
end
delete_slice!(hash, :a, :b)
# => {a: 1, b: 2}
hash
# => {c: 3}
Чистый Рубин:
hash = {a: 1, b: 2, c: 3}
def delete_slice!(hash, *keys)
hash.slice(*keys).tap { keys.each{ hash.delete _1 } }
end
delete_slice!(hash, :a, :b)
# => {a: 1, b: 2}
hash
# => {c: 3}
Несколько способов удалить 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 здесь.
Спасибо
Это однострочный способ сделать это, но он не очень читабелен. Рекомендуется использовать две строки вместо.
use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call)