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方法始终返回调用的对象…
否则,如果您需要
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使用白名单方法,所以它删除所有未列为参数的键。这些方法(
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中获取剩余的哈希。
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} |
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} |
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} |
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) => {} |
您可以从
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。
谢谢你
这也会起作用: