如何从哈希中删除一个密钥,并在Ruby / Rails中获取剩余的哈希值?

要添加一个新的哈希对我做:

{:a => 1, :b => 2}.merge!({:c => 3}) # => {:a=>1, :b=>2, :c=>3} 

有没有类似的方式来从哈希删除密钥?

这工作:

 {: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 } 

在一行中。

Rails除了/除了! 方法返回散列与这些键被删除。 如果您已经在使用Rails,那么创build自己的版本是没有意义的。

 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 

Oneliner平原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! ),它修改给定的散列,但是它们的返回值不同,它们都返回散列。 它代表slice!删除的键slice!except!保留的钥匙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} 

为什么不使用:

 hash.delete(key) 
 #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会返回一个散列的副本,并删除键,同时删除! 修改散列本身。 这符合Ruby的惯例。 例如从控制台

 >> 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) => {} 

有很多方法可以从散列中删除密钥,并在Ruby中获取剩余的散列。

  1. .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} 
  2. .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} 
  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} 
  4. .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),不想创build扩展方法(也许你只需要在一两个地方使用,不想用大量的方法来污染命名空间),并且不想编辑散列到位(即,你像我这样的函数编程的粉丝),你可以'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} 

你可以使用except! 从小facetsgem:

 >> 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 

您可以使用此function而不影响程序的其他部分,或者不得不包含大型外部库。

 class FabulousCode using HashExtensions def incredible_stuff delightful_hash.except(:not_fabulous_key) end end 

在纯Ruby中:

 {:a => 1, :b => 2}.tap{|x| x.delete(:a)} # => {:b=>2} 

请参阅Ruby on Rails:删除多个散列键

 hash.delete_if{ |k,| keys_to_delete.include? k } 

这是一个单行的方式来做到这一点,但它不是很可读。 推荐使用两行代替。

 use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call) 

这也将工作: hash[hey] = nil