web-dev-qa-db-ja.com

Hashからキーを削除し、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 })

一行で。

501
Misha Moroshko

Railsには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
675
Peter Brown

OnelinerプレーンなRuby、それは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 methodは常に呼び出されたオブジェクトを返します...

そうでなければ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!)の強打バージョンも存在します。これは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} 
185
Fabio

単に使用しないでください:

hash.delete(key)
157
dbryson

ハッシュからキーを削除し、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 =>値に基づいてキーを削除する必要がある場合。元のハッシュから一致するキーを明らかに削除します

    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に基づく結果。

57
techdreams

純粋なRuby(Railsなし)を使いたいのであれば、拡張メソッドを作成したくない(多分これは1つか2つの場所でしか必要とせず、たくさんのメソッドで名前空間を汚染したくないでしょう)。ハッシュをその場で編集する(つまり、私のような関数型プログラミングのファンであれば)、 'select':

>> 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}
36
Yura Taras
#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)
=> {}
31
Max Williams

facets gemからexcept!を使用できます。

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

元のハッシュは変わりません。

編集:Russelが言っているように、ファセットにはいくつかの隠れた問題があり、ActiveSupportと完全にAPI互換ではありません。一方、ActiveSupportはファセットほど完成度が高くありません。最後に、私はASを使用し、あなたのコードにEdgeのケースを入れておくことにしました。

27
rewritten

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

純粋なRubyでは:

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

Deleteがハッシュの削除ペアを返す場合、それは素晴らしかったです。私はこれをやっている:

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

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

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

ハッシュキーを削除する複数の方法あなたは下から任意の方法を使用することができます

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のHash/doc here を見てください。

ありがとうございました

0
Ketan Mangukiya