Frage Ruby - Greifen Sie auf multidimensionalen Hash zu und vermeiden Sie den Zugriff nil object [duplizieren]


Mögliche Duplikate:
Ruby: Nils in einer IF-Anweisung
Gibt es einen sauberen Weg, um zu vermeiden, dass eine Methode für nil in einem verschachtelten Params-Hash aufgerufen wird? 

Nehmen wir an, ich versuche auf einen Hash wie folgt zuzugreifen:

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

Das ist schön, wenn key1, key2 und key3 in den Hashes existieren, aber was ist, wenn zB key1 nicht existiert?

Dann würde ich bekommen NoMethodError: undefined method [] for nil:NilClass. Und niemand mag das.

Bisher beschäftige ich mich damit, ein Konditional zu machen wie:

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

Ist das angemessen, gibt es einen anderen rabiaten Weg?


75
2018-04-12 19:50


Ursprung


Antworten:


Es gibt viele Ansätze dazu.

Wenn Sie Ruby 2.3 oder höher verwenden, können Sie verwenden graben

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

Viele Leute bleiben einfach Rubin und Kette die && Wachtests.

Sie könnten stdlib verwenden Hash # holen auch:

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

Einige mögen ActiveSupport verketten #Versuchen Methode.

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

Andere benutzen und und

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

Manche Leute denken egozentrische Nils sind eine gute Idee (obwohl jemand dich jagen und dich foltern könnte, wenn sie dich finden würden).

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

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

Du könntest benutzen Enumerable # reduzieren (oder Alias ​​injizieren).

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

Oder erweitern Sie Hash oder nur Ihr Ziel-Hash-Objekt mit einer geschachtelten Suchmethode

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'

Oh, und wie könnten wir das vergessen? könnte sein Monade?

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

148
2018-04-12 20:36



Du könntest auch benutzen Objekt # undand.

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

6
2018-04-12 20:24



Bedingungen my_hash['key1'] && my_hash['key1']['key2'] fühle mich nicht TROCKEN.

Alternativen:

1) Autovivifizierung Zauber. Von diesem Beitrag:

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

Dann mit deinem Beispiel:

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

Es ähnelt dem Hash.fetch-Ansatz, da beide mit neuen Hashes als Standardwerte arbeiten, aber dadurch werden Details zur Erstellungszeit verschoben. Zugegebenermaßen ist das ein bisschen Betrug: Es wird niemals "Null" geben, nur ein leerer Hash, der spontan erstellt wird. Abhängig von Ihrem Anwendungsfall könnte dies verschwenderisch sein.

2) Extrahieren Sie die Datenstruktur mit ihrem Lookup-Mechanismus und bearbeiten Sie den nicht gefundenen Fall hinter den Kulissen. Ein simples Beispiel:

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) Wenn Sie sich monadisch fühlen, können Sie einen Blick darauf werfen, Könnte sein


5
2018-04-12 19:58