Is there a non-destructive way of deleting a key value pair from a hash?

For example, if you did

original_hash = {:foo => :bar}
new_hash = original_hash
new_hash = new_hash.reject{|key, _| key == :foo}

or

original_hash = {:foo => :bar}
new_hash = original_hash
new_hash = new_hash.dup
new_hash.delete(:foo)

then original_hash is unchanged, and new_hash is changed, but they're a tad verbose. However, if you did

original_hash = {:foo => :bar}
new_hash = original_hash
new_hash.delete(:foo)

then original_hash is changed, which isn't what I want.

Is there a single method that does what I want?

有帮助吗?

解决方案 2

ActiveSupport provides a hash extension: Hash#except. It allows you to return a new hash except specified keys without modifying the original.

Assuming you have installed the active_support gem:

ruby-1.9.3> require 'active_support/core_ext/hash/except.rb'
 => true
 ruby-1.9.3> a = {x: 2, y: 1, z: 3}
 => {:x=>2, :y=>1, :z=>3} 
ruby-1.9.3> b = a.except(:x)
 => {:y=>1, :z=>3} 
ruby-1.9.3> c = a.except(:x, :y)
 => {:z=>3} 
ruby-1.9.3> a
 => {:x=>2, :y=>1, :z=>3} 
ruby-1.9.3> b
 => {:y=>1, :z=>3} 
ruby-1.9.3> c
 => {:z=>3} 

其他提示

Yes, you want reject:

new_hash = original_hash.reject{|key, _| key == :foo}

The problem is that new_hash is a reference unless you explicitly tell Ruby to duplicate the object. You're on the right track using dup, the only thing I might recommend is to do

new_hash = original_hash.dup 

because I think thats more explicit about what you're doing.

What about chaining the dup to make it less verbose?

new_hash = original_hash.dup.delete_if{|key, _| key == :foo}
original_hash.clone.tap {|h| h.delete key}

tap is arguably less clunky than declaring a new variable, mostly because it's on the same line.

clone is, I think, more explicitly a flat copy than dup, though of course for hashes it makes little difference.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top