Reputation: 171341
To add a new pair to Hash I do:
{:a => 1, :b => 2}.merge!({:c => 3}) #=> {:a => 1, :b => 2, :c => 3}
Is there a similar way to delete a key from Hash ?
This works:
{:a => 1, :b => 2}.reject! { |k| k == :a } #=> {:b => 2}
but I would expect to have something like:
{:a => 1, :b => 2}.delete!(:a) #=> {:b => 2}
It is important that the returning value will be the remaining hash, so I could do things like:
foo(my_hash.reject! { |k| k == my_key })
in one line.
Upvotes: 691
Views: 678241
Reputation: 5813
I want to delete a list of keys, and get back the deleted "slice" of the hash:
Rails:
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}
Pure Ruby:
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}
Upvotes: 2
Reputation: 391
Use delete
, except
, or except!
sample_hash = {hey: 'hey', hello: 'hello'}
Delete:
sample_hash.delete(:hey)
=> 'hey'
sample_hash
=> {hello: 'hello'}
Returns value of the key and deletes the key in the original object, returns nil if no such key
Except:
sample_hash.except(:hey)
=> {hello: 'hello'}
sample_hash
=> {hey: 'hey', hello: 'hello'}
Returns the entire hash without the specified keys, but does not update the original hash
Except!:
except!
is the same as except but it permanently changes the state of the original hash like all bang operated methods do
sample_hash.except!(:hey)
=> {hello: 'hello'}
sample_hash
=> {hello: 'hello'}
Upvotes: 7
Reputation: 51697
For those of you who just came here to know how to delete a key/value pair from a hash, you can use:
hash.delete(key)
For the rest of you who came here to read a wall of text about something entirely different, you can read the rest of this answer:
Rails has an except/except! method that returns the hash with those keys removed. If you're already using Rails, there's no sense in creating your own version of this.
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
Upvotes: 917
Reputation: 9238
Starting from Ruby 3.0, Hash#except is a build-in method.
As a result, there is no more need to depend on ActiveSupport or write monkey-patches in order to use it.
h = { a: 1, b: 2, c: 3 }
p h.except(:a) #=> {:b=>2, :c=>3}
Sources:
Upvotes: 38
Reputation: 6127
Why not just use:
hash.delete(key)
hash
is now the "remaining hash" you're looking for.
Upvotes: 262
Reputation: 1098
Try the except!
method.
{:a => 1, :b => 2}.except!(:a) #=> {:b => 2}
Upvotes: 4
Reputation: 5585
There are many ways to remove a key from a hash and get the remaining hash in Ruby.
.slice
=> It will return selected keys and not delete them from the original hash. Use slice!
if you want to remove the keys permanently else use simple 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
=> It will delete the selected keys from the original hash(it can accept only one key and not more than one).
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
=> It will return the remaining keys but not delete anything from the original hash. Use except!
if you want to remove the keys permanently else use simple 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
=> In case you need to remove a key based on a value. It will obviously remove the matching keys from the original hash.
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
=> It is used to remove all nil
values from the hash. Use compact!
if you want to remove the nil
values permanently else use simple 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}
Results based on Ruby 2.2.2.
Upvotes: 139
Reputation: 418
Multiple ways to delete Key in Hash. you can use any Method from below
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
So many ways is there, you can look on Ruby doc of Hash here.
Thank you
Upvotes: 1
Reputation: 703
It's was great if delete return the delete pair of the hash. I'm doing this:
hash = {a: 1, b: 2, c: 3}
{b: hash.delete(:b)} # => {:b=>2}
hash # => {:a=>1, :c=>3}
Upvotes: 5
Reputation: 35349
Instead of monkey patching or needlessly including large libraries, you can use refinements if you are using 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
You can use this feature without affecting other parts of your program, or having to include large external libraries.
class FabulousCode
using HashExtensions
def incredible_stuff
delightful_hash.except(:not_fabulous_key)
end
end
Upvotes: 21
Reputation: 35074
See Ruby on Rails: Delete multiple hash keys
hash.delete_if{ |k,| keys_to_delete.include? k }
Upvotes: 14
Reputation: 1766
This is a one line way to do it, but it's not very readable. Recommend using two lines instead.
use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call)
Upvotes: 1
Reputation: 1293
If you want to use pure Ruby (no Rails), don't want to create extension methods (maybe you need this only in one or two places and don't want to pollute namespace with tons of methods) and don't want to edit hash in place (i.e., you're fan of functional programming like me), you can 'select':
>> 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}
Upvotes: 39
Reputation: 16435
You can use except!
from the facets
gem:
>> require 'facets' # or require 'facets/hash/except'
=> true
>> {:a => 1, :b => 2}.except(:a)
=> {:b=>2}
The original hash does not change.
EDIT: as Russel says, facets has some hidden issues and is not completely API-compatible with ActiveSupport. On the other side ActiveSupport is not as complete as facets. In the end, I'd use AS and let the edge cases in your code.
Upvotes: 27
Reputation: 3859
in pure Ruby:
{:a => 1, :b => 2}.tap{|x| x.delete(:a)} # => {:b=>2}
Upvotes: 18
Reputation: 19176
Oneliner plain ruby, it works only with 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 method always return the object on which is invoked...
Otherwise if you have required active_support/core_ext/hash
(which is automatically required in every Rails application) you can use one of the following methods depending on your needs:
➜ ~ 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 uses a blacklist approach, so it removes all the keys listed as args, while slice uses a whitelist approach, so it removes all keys that aren't listed as arguments. There also exist the bang version of those method (except!
and slice!
) which modify the given hash but their return value is different both of them return an hash. It represents the removed keys for slice!
and the keys that are kept for the 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}
Upvotes: 246
Reputation: 32933
#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'
I've set this up so that .remove returns a copy of the hash with the keys removed, while remove! modifies the hash itself. This is in keeping with ruby conventions. eg, from the console
>> 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)
=> {}
Upvotes: 30