2011-06-03 114 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