ChatGPT解决这个技术问题 Extra ChatGPT

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

要向哈希添加一个新对,我会:

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

有没有类似的方法从 Hash 中删除一个键?

这有效:

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

在一行中。

如果您确实需要,您可以随时扩展(在运行时打开)内置 Hash 以添加此自定义方法。

P
Peter Brown

Rails has an except/except! method 返回删除了这些键的哈希。如果您已经在使用 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

您不必使用完整的 Rails 堆栈。您可以在任何 Ruby 应用程序中包含 ActiveSupport。
要添加到 Fryie 的答案,您甚至不需要加载所有 ActiveSupport;你可以只包括它们然后require "active_support/core_ext/hash/except"
编辑太晚了:我的意思是“包括宝石”而不是“包括它们”
@GMA:当你的五分钟编辑时间结束时,你可以随时复制、删除、修改和重新发布评论。
F
Fabio

Oneliner 纯红宝石,它仅适用于 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 使用黑名单方法,因此它会删除所有列为 args 的键,而 slice 使用白名单方法,因此它会删除所有未列为参数的键。也存在那些修改给定哈希但返回值不同的方法(except!slice!)的 bang 版本,它们都返回一个哈希。它表示 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} 

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

为什么不直接使用:

hash.delete(key)

hash 现在是您要查找的“剩余哈希”。


@dbryson:我同意有时不值得。我只是想知道为什么有 mergemerge!delete,但没有 detele!...
如果你真的需要它作为一个班轮做:foo(hash.delete(key) || hash)
如果 delete 没有修改它的参数并且如果 delete! 存在并且修改了它的参数,它将更符合 Ruby 约定。
这不会返回问题中提到的剩余哈希,它将返回与已删除键关联的值。
@大卫J。与流行的看法相反,感叹号并不表示突变,而是表示“异常行为”。我想没有 delete!,因为变异的键删除方法似乎是唯一的“预期”行为。
t
techdreams

有很多方法可以从散列中删除一个键并在 Ruby 中获取剩余的散列。

.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} .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} .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} .delete_if => 如果您需要根据值删除键。它显然会从原始哈希中删除匹配的键。 2.2.2 :115 > hash = {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1} => {"one"=>1, "two" =>2, "三"=>3, "one_again"=>1} 2.2.2 :116 > 值 = 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} .compact =>它用于从哈希中删除所有 nil 值。使用紧凑型!如果您想永久删除 nil 值,请使用简单紧凑。 2.2.2 :119 > hash = {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil} => {"one" =>1, "二"=>2, "三"=>3, "无"=>nil, "no_value"=>nil} 2.2.2 :120 > hash.compact => {"one"=>1 , "二"=>2, "三"=>3}

结果基于 Ruby 2.2.2。


sliceexcept 是使用 ActiveSupport::CoreExtensions::Hash 添加的。它们不是 Ruby 核心的一部分。 require 'active_support/core_ext/hash' 可以使用它们
由于 Ruby 2.5 Hash#slice 在标准库中。 ruby-doc.org/core-2.5.0/Hash.html#method-i-slice 好!
谢谢你非常全面的回答。
Y
Yura Taras

如果你想使用纯 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}

M
Max Williams
#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)
=> {}

r
rewritten

您可以使用 facets gem 中的 except!

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

原始哈希不会改变。

编辑:正如 Russel 所说,facets 有一些隐藏的问题,并且与 ActiveSupport 的 API 不完全兼容。另一方面,ActiveSupport 不如 facets 完整。最后,我会使用 AS 并让您的代码中出现边缘情况。


只是 require 'facets/hash/except',它们不是“问题”(除了不是 100% AS API 之外,不知道它们会是什么问题)。如果你使用 AS 做一个 Rails 项目是有意义的,如果不是,Facets 的占用空间要小得多。
@trans ActiveSupport 现在的占用空间也很小,您可以只需要其中的一部分。就像方面一样,但有更多人关注它(所以我想它会得到更好的评论)。
M
Mohamad

您可以使用 refinements if you are using 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

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

class FabulousCode
  using HashExtensions

  def incredible_stuff
    delightful_hash.except(:not_fabulous_key)
  end
end

M
Marian13

Hash#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}

资料来源:

Hash#except 来自官方 Ruby 文档。

链接到 PR。

Ruby 3.0 添加了 Hash#except 和 ENV.except。


g
gamov

在纯红宝石中:

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

C
Community

请参阅Ruby on Rails: Delete multiple hash keys

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

keys_to_delete.each {|k| hash.delete(k)} 对于大型数据集要快得多。如果错了,请纠正我。
@VigneshJayavel,你是对的,但 OP 希望返回哈希值。 each 将返回数组。
f
frenesim

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

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

S
Sachin Singh

尝试 except! 方法。

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

f
frediy

这是一种单行的方式,但它的可读性不是很高。建议改用两条线。

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

Hash#exceptHash#except! 已经被提及得够多了。正如您所提到的,Proc.new 版本不是很易读,而且比 use_remaining_hash_for_something(begin hash.delete(:key); hash end) 更复杂。也许只是删除这个答案。
缩短了我的答案并删除了已经说过的话。将我的回答与您的评论一起保留,因为他们回答了问题并提出了很好的使用建议。
K
Ketan Mangukiya

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

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

有很多方法,您可以查看 Hash here 的 Ruby 文档。

谢谢


f
fdghdfg

这也可以:hash[hey] = nil


h = {:a => 1, :b => 2, :c => 3}; h[:a]=无; h.每个{|k,v| puts k} 不等于:h = {:a => 1, :b => 2, :c => 3}; h.delete(:a); h.每个{|k,v|放 k}
从散列中删除键与从散列中删除键的值不同。由于这可能会使人们感到困惑,因此最好删除此答案。