ハッシュからキーを削除し、Ruby / Railsの残りのハッシュを取得する方法は?


560

ハッシュに新しいペアを追加するには、次のようにします。

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

回答:


750

Railsにはexcept / exceptがあります!これらのキーが削除されたハッシュを返すメソッド。すでに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

51
完全なRailsスタックを使用する必要はありません。RubyアプリケーションにActiveSupportを含めることができます。
Fryie 2013

10
Fryieの答えに追加するには、ActiveSupportをすべてロードする必要すらありません。それらを含めるだけでよいrequire "active_support/core_ext/hash/except"
GMA

編集するには遅すぎます:「gemを含める」ではなく「gemを含める」という意味でした
GMA

@GMA:5分の編集が終わったら、いつでもコメントをコピー、削除、変更、再投稿できます。
iconoclast

212

ワンライナーの単純なルビで、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!and 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} 

18
+1このメソッドはで破壊的であることを言及する価値がありhます。Hash#except元のハッシュは変更されません。
ありがとう

3
h.dup.tap { |hs| hs.delete(:a) }元のハッシュを変更しないようにするために使用します。
Magicode 2018年

181

なぜ使用しないのですか?

hash.delete(key)

2
@dbryson:時々それは価値がないことに同意します。私はちょうど疑問がある理由をmergemerge!delete、ないdetele!...
ミーシャMoroshko

1
:あなたは本当に1つのライナーそうであるように、それを必要とする場合foo(hash.delete(key) || hash)
バートGoethals

13
パラメータを変更deleteなかった場合、およびdelete!存在してパラメータを変更した場合は、Rubyの規則との整合性が向上します。
David J.

60
質問で述べたように、残りのハッシュは返されません。削除されたキーに関連付けられた値が返されます。
MhdSyrwan、2015

1
deleteはキーを返しますが、ハッシュも変更します。削除がない理由については、私の考えでは、ある意味で削除を呼び出すのは意味的に意味がなく、実際には削除しないのではないでしょうか。hash.delete!()とは対照的に、hash.delete()の呼び出しは何もしません。
eggmatters

85

Rubyでハッシュからキーを削除して残りのハッシュを取得する方法はたくさんあります。

  1. .slice=>選択したキーを返し、元のハッシュからそれらを削除しません。slice!キーを完全に削除する場合は、simpleを使用します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 =>選択したキーを元のハッシュから削除します(1つのキーのみを受け入れ、複数のキーは受け入れられません)。

    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=>残りのキーを返しますが、元のハッシュからは何も削除しません。except!キーを完全に削除する場合は、simpleを使用します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=>値に基づいてキーを削除する必要がある場合。それは明らかに元のハッシュから一致するキーを削除します。

    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} 
  5. .compact=> nilハッシュからすべての値を削除するために使用されます。値を完全compact!に削除したい場合は使用し、そうでない場合nilはsimpleを使用しますcompact

    2.2.2 :119 > hash = {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil}
     => {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil} 
    2.2.2 :120 > hash.compact
     => {"one"=>1, "two"=>2, "three"=>3}

Ruby 2.2.2に基づく結果。


16
sliceおよびexceptを使用して追加されActiveSupport::CoreExtensions::Hashます。これらはRubyコアの一部ではありません。それらは次のユーザーが使用できますrequire 'active_support/core_ext/hash'
MadisNõmme16年

3
Ruby 2.5 Hash#sliceは標準ライブラリに含まれているため。ruby-doc.org/core-2.5.0/Hash.html#method-i-slice Yay!
MadisNõmme19年

38

純粋なRuby(Railsなし)を使用したい場合は、拡張メソッドを作成しないでください(1つか2つの場所でのみ必要で、大量のメソッドで名前空間を汚染したくない場合があります)。その場でハッシュを編集します(つまり、私のような関数型プログラミングのファンです)。「選択」できます。

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

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'

.removeが削除されたキーを削除したハッシュのコピーを返すようにこれを設定しましたが、削除します!ハッシュ自体を変更します。これはルビの慣例に沿ったものです。たとえば、コンソールから

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

元のハッシュは変更されません。

編集:ラッセルが言うように、ファセットにはいくつかの隠れた問題があり、ActiveSupportと完全にAPI互換ではありません。一方、ActiveSupportはファセットほど完全ではありません。最後に、私はASを使用して、コードのエッジケースを許可します。


ただrequire 'facets/hash/except'、それらは「問題」ではありません(100%AS API以外ではどういう問題になるのかわからない)。ASを使用してRailsプロジェクトを実行している場合、ファセットのフットプリントははるかに小さくなります。
トランス

@trans ActiveSupportも最近はフットプリントがかなり小さく、必要なのはその一部のみです。ファセットと同じですが、より多くの目を向けています(そのため、より良いレビューが得られると思います)。
書き換え

19

モンキーパッチを適用したり、不必要に大きなライブラリを含めたりする代わりに、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

17

純粋なRubyでは:

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

13

Ruby on Rails:複数のハッシュキーを削除するをご覧ください

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

keys_to_delete.each {| k | hash.delete(k)}は、大規模なデータセットに対してはるかに高速です。間違っていれば訂正してください。
Vignesh Jayavel

@VigneshJayavel、あなたは正しいですが、OPはハッシュを返すことを望んでいました。each配列を返します。
Nakilon、

3

deleteがハッシュの削除ペアを返すのは素晴らしいことです。私はこれをやっています:

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

1

これは1行で実行できますが、あまり読みやすいものではありません。代わりに2行を使用することをお勧めします。

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

1
Hash#exceptそしてHash#except!、既に十分に言及されています。Proc.newあなたが言及しても、以上の複雑としてバージョンは非常に読みやすいではありませんuse_remaining_hash_for_something(begin hash.delete(:key); hash end)。たぶんこの答えを削除してください。
Michael Kohl 2014年

1
私の答えを短くして、すでに言われていたものを削除しました。彼らが質問に答えて、使用のための良い推薦をするので、あなたのコメントとともに私の答えを保つ。
the_minted 2014年

0

ハッシュ内のキーを削除する複数の方法。下から任意のメソッドを使用できます

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

非常に多くの方法があるので、ここでハッシュのRubyドキュメントを見ることができます。

ありがとうございました


-12

これも機能します: hash[hey] = nil


3
h = {:a => 1、:b => 2、:c => 3}; h [:a] = nil; h.each {| k、v | puts k}次と同じではありません:h = {:a => 1、:b => 2、:c => 3}; h.delete(:a); h.each {| k、v | puts k}
obaqueiro 2014年

1
ハッシュからキーを削除することは、ハッシュからキーの値を削除することと同じではありません。これは人々を混乱させる可能性があるので、この答えを削除することをお勧めします。
セバスチャンパルマ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.