如何从Hash中删除一个键并获取Ruby/Rails中的剩余哈希?

Mis*_*hko 531 ruby ruby-on-rails hashmap ruby-on-rails-3 ruby-hash

要向Hash添加新对,我会:

{:a => 1, :b => 2}.merge!({:c => 3})   #=> {:a => 1, :b => 2, :c => 3}
Run Code Online (Sandbox Code Playgroud)

是否有类似的方法从哈希删除密钥?

这有效:

{:a => 1, :b => 2}.reject! { |k| k == :a }   #=> {:b => 2}
Run Code Online (Sandbox Code Playgroud)

但我希望有类似的东西:

{:a => 1, :b => 2}.delete!(:a)   #=> {:b => 2}
Run Code Online (Sandbox Code Playgroud)

重要的是返回值将是剩余的哈希值,因此我可以执行以下操作:

foo(my_hash.reject! { |k| k == my_key })
Run Code Online (Sandbox Code Playgroud)

在一条线上.

Pet*_*own 709

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
Run Code Online (Sandbox Code Playgroud)

  • 您不必使用完整的Rails堆栈.您可以在任何Ruby应用程序中包含ActiveSupport. (47认同)
  • @Obromios` {a:1,b:2} .except!(:a,:b)`是正确的 (10认同)
  • 要添加到Fryie的答案,您甚至不需要加载所有的ActiveSupport; 你可以只包括它们然后`require"active_support/core_ext/hash/except"` (7认同)
  • @GMA:当您的五分钟编辑时间结束后,您可以随时复制、删除、修改和重新发布评论。 (2认同)

Fab*_*bio 197

Oneliner普通红宝石,它只适用于红宝石> 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} 
Run Code Online (Sandbox Code Playgroud)

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} 
Run Code Online (Sandbox Code Playgroud)

除了使用黑名单方法,因此它删除列为args的所有键,而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} 
Run Code Online (Sandbox Code Playgroud)

  • +1值得一提的是,这种方法对`h`具有破坏性.`Hash#except`不会修改原始哈希值. (18认同)
  • 使用`h.dup.tap {| hs | hs.delete(:a)}`,以避免修改原始哈希。 (3认同)

dbr*_*son 172

为什么不使用:

hash.delete(key)
Run Code Online (Sandbox Code Playgroud)

  • 这不会返回问题中提到的剩余哈希值,它将返回与已删除键关联的值. (56认同)
  • 如果`delete`没有**修改它的参数,如果`delete!`存在并修改了它的参数,它将与Ruby约定更加一致. (13认同)
  • @大卫J。与普遍的看法相反,感叹号并不表示突变,而是表示“异常行为”。我想没有“删除!”,因为突变的键删除方法似乎是唯一的“预期”行为。 (4认同)
  • @dbryson:我同意有时它不值得.我只是想知道为什么有`merge`,`merge!`,`delete`,但没有'狡猾!`...... (2认同)
  • 这个答案完全错误。该问题明确指出“返回值将是剩余的哈希值很重要”。Hash.delete() 不返回哈希值。非常不幸的是,它改变了哈希值。正如其他人所指出的,它应该是在 merge 和 merge! 之后建模的。 (2认同)

tec*_*ams 72

有很多方法可以从哈希中删除密钥并在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} 
    
    Run Code Online (Sandbox Code Playgroud)
  2. slice! =>它将从原始哈希中删除所选键(它只能接受一个键而不能超过一个键)

    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} 
    
    Run Code Online (Sandbox Code Playgroud)
  3. slice =>它将返回剩余的键但不删除原始哈希中的任何内容

    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}         
    
    Run Code Online (Sandbox Code Playgroud)
  4. .delete=>如果您需要根据值删除密钥.它显然会从原始哈希中删除匹配的键

    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} 
    
    Run Code Online (Sandbox Code Playgroud)

结果基于Ruby 2.2.2.

  • 使用`ActiveSupport :: CoreExtensions :: Hash`添加`slice`和`except`.它们不是Ruby核心的一部分.它们可以被`require'active_support/core_ext/hash'`使用 (15认同)
  • 从 Ruby 2.5 开始,`Hash#slice` 已包含在标准库中。https://ruby-doc.org/core-2.5.0/Hash.html#method-i-slice 耶! (6认同)

Yur*_*ras 38

如果你想使用纯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}
Run Code Online (Sandbox Code Playgroud)


Max*_*ams 31

#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'
Run Code Online (Sandbox Code Playgroud)

我已经设置了这个,以便.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)
=> {}
Run Code Online (Sandbox Code Playgroud)


rew*_*ten 27

您可以使用except!facets宝石:

>> require 'facets' # or require 'facets/hash/except'
=> true
>> {:a => 1, :b => 2}.except(:a)
=> {:b=>2}
Run Code Online (Sandbox Code Playgroud)

原始哈希不会改变.

编辑:正如Russel所说,facets存在一些隐藏的问题,并且与ActiveSupport不完全API兼容.另一方面,ActiveSupport并不像facet那样完整.最后,我使用AS并在代码中包含边缘情况.


Moh*_*mad 19

如果您使用的是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
Run Code Online (Sandbox Code Playgroud)

您可以使用此功能,而不会影响程序的其他部分,或者必须包含大型外部库.

class FabulousCode
  using HashExtensions

  def incredible_stuff
    delightful_hash.except(:not_fabulous_key)
  end
end
Run Code Online (Sandbox Code Playgroud)


gam*_*mov 17

在纯Ruby中:

{:a => 1, :b => 2}.tap{|x| x.delete(:a)}   # => {:b=>2}
Run Code Online (Sandbox Code Playgroud)


Mar*_*n13 14

哈希#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}
Run Code Online (Sandbox Code Playgroud)

资料来源:


Nak*_*lon 12

请参阅Ruby on Rails:删除多个哈希键

hash.delete_if{ |k,| keys_to_delete.include? k }
Run Code Online (Sandbox Code Playgroud)


fre*_*sim 5

如果删除返回哈希的删除对,那就太好了。我正在这样做:

hash = {a: 1, b: 2, c: 3}
{b: hash.delete(:b)} # => {:b=>2}
hash  # => {:a=>1, :c=>3} 
Run Code Online (Sandbox Code Playgroud)