2012-04-12 13 views
70

可能性の重複:
Ruby: Nils in an IF statement
Is there a clean way to avoid calling a method on nil in a nested params hash?ルビー - アクセス多次元ハッシュと避けるアクセスnilのオブジェクト

のは、私はこのようなハッシュアクセスしてみましょう:

my_hash['key1']['key2']['key3'] 

これは、key1、ke y2とkey3はハッシュに存在しますが、たとえばkey1が存在しない場合はどうなりますか?

次に、NoMethodError: undefined method [] for nil:NilClassを取得します。そして誰もそれを好きではない。

これまでのところ、私はこの条件などをやって対処:

if my_hash['key1'] && my_hash['key1']['key2'] ...

はこの適切です、そうすることの他Rubiestの方法は何ですか?

+1

すみません。私はこれを探してみて、それを見つけることができませんでした。適切な場合は閉じてください。 – Nobita

+0

受け入れられた答えには、Ruby 2.3+の正しい方法以外のすべての可能な方法が記載されています。http://ruby-doc.org/core-2.3.1/Hash.html#method-i-dig –

答えて

139

これには多くのアプローチがあります。あなたはルビー2.3以上を使用する場合

、あなたは平野ルビーとチェーン&&ガード・テストにこだわる人々のdig

my_hash.dig('key1', 'key2', 'key3') 

プレンティを使用することができます。

あなたもSTDLIB Hash#fetchを使用することができます。

my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil) 

一部activesupportのの#tryメソッドをチェーンのように。

my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3') 

その他andand

myhash['key1'].andand['key2'].andand['key3'] 

を使用する一部の人々はegocentric nilsは(誰かがあなたを追い詰めると、彼らはあなたがこれを行う見つかった場合は、あなたを苦しめるかもしれませんが)良いアイデアだと思います。

class NilClass 
    def method_missing(*args); nil; end 
end 

my_hash['key1']['key2']['key3'] 

Enumerable#reduce(またはエイリアスインジェクション)を使用できます。

['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] } 

それとも、ネストされたルックアップ法でハッシュまたはちょうどあなたのターゲットハッシュオブジェクトを拡張

module NestedHashLookup 
    def nest *keys 
    keys.reduce(self) {|m,k| m && m[k] } 
    end 
end 

my_hash.extend(NestedHashLookup) 
my_hash.nest 'key1', 'key2', 'key3' 

ああ、どのように我々はmaybeモナドを忘れることができますか?

Maybe.new(my_hash)['key1']['key2']['key3'] 
+1

私はフェッチ方法をとることにしました。しかし、すべての提案に感謝します。 – Nobita

+0

例外処理に役立つ多分(そして他のモナド)モナド宝石を試すこともできます –

+0

ステートメントの最後に 'rescue nil'を使用することについてのあなたの考えは? – jakeonrails

5

条件my_hash['key1'] && my_hash['key1']['key2'] do not feel feel DRY

代替:

1)autovivification魔法。

def autovivifying_hash 
    Hash.new {|ht,k| ht[k] = autovivifying_hash} 
end 

次に、あなたの例で:そのポストから

my_hash = autovivifying_hash  
my_hash['key1']['key2']['key3'] 

それはそれでHash.fetchのアプローチに似て、両方のデフォルト値として新しいハッシュで動作しますが、これは創造に詳細を移動時間。 確かに、これはちょっとした不正行為です:空で作成された空のハッシュだけを 'nil'に戻すことは決してありません。ユースケースによっては、これは無駄かもしれません。

2)データ構造をルックアップメカニズムで抽象化し、シーンの背後で見つからないケースを処理します。単純な例:

def lookup(model, key, *rest) 
    v = model[key] 
    if rest.empty? 
     v 
    else 
     v && lookup(v, *rest) 
    end 
end 
##### 

lookup(my_hash, 'key1', 'key2', 'key3') 
=> nil or value 

3)あなたがモナドと感じた場合は、これを見てとることができ、Maybe