2011-06-03 111 views
417

要添加新的对哈希我做的:如何从哈希中删除密钥并在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 }) 

在一行中。

+1

如果您真的需要,您可以随时扩展(在运行时打开)内置的哈希以添加此自定义方法。 – dbryson 2011-06-03 13:45:40

回答

584

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

您不必使用完整的Rails堆栈。您可以在任何Ruby应用程序中包含ActiveSupport。 – Fryie 2013-09-27 15:46:39

+19

只针对那些喜欢明确答案的人'''{a:1,b:2} .except!(:a)'''如果有多个键被删除'''{a:1 ,b:2} .except!(a :, b:)''' – Obromios 2016-02-18 20:17:50

+6

@Obromios'{a:1,b:2} .except!(:a,:b)'是正确的 – 2016-03-29 14:16:51

132

为什么不直接使用:

hash.delete(key) 
+0

我想做'foo(h.reject!{| k | k ==:a})''。有了你的建议,我必须分两行来做。 – 2011-06-03 13:33:35

+2

@dbryson:我同意有时它不值得。我只是想知道为什么有'merge','merge!','delete',但是没有'detele!'...... – 2011-06-03 13:49:58

+1

如果你真的需要它作为一个班轮的话:'foo(hash.delete(key) | hash)' – 2011-06-04 12:40:10

30
#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的惯例。例如,从控制台

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

您可以使用except!facets宝石:

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

原来的哈希值不改变。

编辑:正如罗素所说,方面有一些隐藏的问题,并不完全API与ActiveSupport兼容。另一方面,ActiveSupport不如完整面。最后,我会使用AS并让代码中的边缘案例。

+0

只需要'facets/hash/except''和它们不是“问题”(不知道它们会是什么问题,除非不是100%的AS API)。如果你正在使用AS做一个Rails项目是有道理的,如果不是Facets有更小的占用空间。 – trans 2016-01-05 17:36:26

+0

@trans现在ActiveSupport的占用空间也非常小,而且您只需要其中的一部分。就像小平面,但有更多的眼睛(所以我认为它得到更好的评论)。 – rewritten 2016-01-06 18:23:01

162

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} 

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使用白名单方法,因此它将删除未作为参数列出的所有键。还有这些方法(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} 
+1

很棒的回答。顺便说一句,'tap'和'delete'在我的ree-1.8.7中工作。 – clacke 2013-04-19 06:12:10

+14

+1值得一提的是这种方法对'h'具有破坏性。 'Hash#except'不会修改原始散列。 – naomik 2013-07-22 19:15:40

14
在纯Ruby

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

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

这是一种行之有效的方式,但它不太可读。建议使用两行代替。

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

'散列#除'和散列#除外!已被足够提及。你提到的'Proc.new'版本不太可读,并且比'use_remaining_hash_for_something(begin hash.delete(:key); hash end)'更复杂。也许只是删除这个答案。 – 2014-01-12 16:13:29

+1

缩短了我的答案,并删除了已经说过的话。保留我的答案以及您的评论,因为他们回答了问题并提出了很好的使用建议。 – 2014-01-14 12:00:31

-6

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

+3

h = {:a => 1,:b => 2,:c => 3}; H [:A] =零; h.each {| K,V |与k} 不一样: h = {:a => 1,:b => 2,:c => 3}; h.delete(:A); h.each {| K,V |把k} – obaqueiro 2014-05-02 20:48:34

10

Ruby on Rails: Delete multiple hash keys

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

keys_to_delete.each {| k | hash.delete(k)}对于大型数据集来说要快得多。纠正我,如果错了。 – 2015-12-29 10:13:56

18

取而代之的猴子打补丁的或不必要的,包括大型图书馆,你可以使用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 

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

class FabulousCode 
    using HashExtensions 

    def incredible_stuff 
    delightful_hash.except(:not_fabulous_key) 
    end 
end 
35

有许多方法可以从散列中删除密钥并在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 =>如果你需要删除基于一个价值的关键nything。从原来的哈希基于Ruby 2.2.2这显然会删除匹配的密钥

    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} 
    

结果。

+7

'slice'和'except'是通过使用'ActiveSupport :: CoreExtensions :: Hash'添加的。它们不是Ruby核心的一部分。它们可以被'require'active_support/core_ext/hash''使用 – 2016-10-09 18:45:10