从哈希表中删除键的非破坏性方法

9
有没有一种非破坏性的方式从哈希表中删除键值对?
例如,如果您执行了以下操作:
original_hash = {:foo => :bar}
new_hash = original_hash
new_hash = new_hash.reject{|key, _| key == :foo}

或者

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

如果你执行了这个操作,那么original_hash不会发生变化,而new_hash会改变,但它们可能有些啰嗦。如果你愿意,可以这样做。

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

那么original_hash被更改了,这不是我想要的。

有没有一个单一的方法能够做到我想要的呢?


2
在你的第一个示例中使用delete_if会更改original_hash。此外,在你的第二个示例中使用deletenew_hash会获取与:foo关联的值,即:bar,而不是哈希本身。 - Chris Salzberg
5个回答

14

是的,你想要 reject

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

11

ActiveSupport提供了一个哈希扩展:Hash#except。它允许你返回一个新的哈希,除了指定的键之外,不修改原始哈希。

假设已经安装了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} 

1
我不使用ActiveSupport,但他们提供这样一个方法的事实是很好的证明Ruby本身没有这样的方法。 - Andrew Grimm

1
问题在于,除非您明确告诉 Ruby 复制对象,否则 new_hash 是一个引用。使用 dup 是正确的方法,我建议您这样做。
new_hash = original_hash.dup 

因为我认为这更明确地说明了你正在做什么。


0
怎么样将dup链接起来以减少冗长?
new_hash = original_hash.dup.delete_if{|key, _| key == :foo}

0
original_hash.clone.tap {|h| h.delete key}

tap 可能比声明一个新变量更简洁,主要是因为它在同一行。

clone 被认为比dup更明确地表示一份完整的复制,尽管对于哈希表来说,这几乎没有任何区别。


虽然这段代码片段可能解决了问题,但是包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议的原因。 - DimaSan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接