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

How to remove a key from Hash and get the remaining hash in Ruby/Rails?

要向哈希添加新对,我需要:

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

是否有类似的方法从哈希中删除键?

这工作:

1
{:a => 1, :b => 2}.reject! { |k| k == :a }   #=> {:b => 2}

但我希望有这样的东西:

1
{:a => 1, :b => 2}.delete!(:a)   #=> {:b => 2}

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

1
foo(my_hash.reject! { |k| k == my_key })

一行。


Rails有一个except/except!方法返回已删除这些键的哈希。如果您已经在使用Rails,那么创建您自己的版本是没有意义的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
2
3
4
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应用程序中都是自动需要的),您可以根据需要使用以下方法之一:

1
2
3
4
5
6
7
8
?  ~  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}

除了使用黑名单方法,所以它删除所有列为参数的键,而slice使用白名单方法,所以它删除所有未列为参数的键。这些方法(except!slice!的bang版本也存在,它们修改了给定的散列,但返回值不同,两者都返回散列。它表示为slice!删除的密钥和为except!保留的密钥:

1
2
3
4
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}


为什么不直接使用:

1
hash.delete(key)


有很多方法可以从哈希中删除键并在Ruby中获取剩余的哈希。

  • .slice=>它将返回所选键,而不会从原始哈希中删除它们。

    1
    2
    3
    4
    5
    6
    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=>将从原始哈希中删除所选键(只能接受一个键,不能接受多个键)

    1
    2
    3
    4
    5
    6
    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=>它将返回剩余的键,但不会从原始哈希中删除任何内容。

    1
    2
    3
    4
    5
    6
    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=>以防需要根据值删除密钥。它显然会从原始哈希中删除匹配的键

    1
    2
    3
    4
    5
    6
    7
    8
    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),不想创建扩展方法(也许您只需要在一个或两个地方使用它,不想用大量的方法污染命名空间),也不想就地编辑哈希(即您是我这样的函数式编程迷),您可以选择:

    1
    2
    3
    4
    5
    6
    7
    8
    >> 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}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #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'

    我已将此设置为。移除将返回哈希的副本,同时移除键!修改哈希本身。这符合Ruby惯例。例如,从控制台

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    >> 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)
    => {}


    您可以从facetsgem使用except!

    1
    2
    3
    4
    >> require 'facets' # or require 'facets/hash/except'
    => true
    >> {:a => 1, :b => 2}.except(:a)
    => {:b=>2}

    原始哈希值不变。

    编辑:正如Russel所说,方面有一些隐藏的问题,并且不是完全与ActiveSupport兼容的API。另一方面,activesupport不如facets那么完整。最后,我将在您的代码中使用as和let。


    如果您使用的是Ruby2,那么您可以使用改进,而不是猴子修补或不必要地包含大型库:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    module HashExtensions
      refine Hash do
        def except!(*candidates)
          candidates.each { |candidate| delete(candidate) }
          self
        end

        def except(*candidates)
          dup.remove!(candidates)
        end
      end
    end

    您可以使用此功能,而不影响程序的其他部分,也不必包括大型外部库。

    1
    2
    3
    4
    5
    6
    7
    class FabulousCode
      using HashExtensions

      def incredible_stuff
        delightful_hash.except(:not_fabulous_key)
      end
    end

    纯露比:

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

    参见RubyonRails:删除多个哈希键

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


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

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


    这是一种单行的方法,但是它不太可读。建议使用两行。

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


    有多种方法可以删除哈希中的键。你可以使用下面的任何方法

    1
    2
    3
    4
    5
    6
    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

    有很多方法,你可以在这里查看RubyDocofHash。

    谢谢你


    这也会起作用:hash[hey] = nil