Pertanyaan Ruby - Akses hash multidimensi dan hindari mengakses objek nil [duplikat]


Kemungkinan Duplikat:
Ruby: Nils dalam pernyataan IF
Apakah ada cara yang bersih untuk menghindari memanggil metode pada nihil dalam params params hash? 

Katakanlah saya mencoba mengakses hash seperti ini:

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

Ini bagus jika key1, key2 dan key3 ada di hash (es), tetapi bagaimana jika, misalnya key1 tidak ada?

Maka saya akan mendapatkan NoMethodError: undefined method [] for nil:NilClass. Dan tidak ada yang suka itu.

Sejauh ini saya berurusan dengan ini melakukan kondisional seperti:

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

Apakah ini tepat, apakah ada cara Rubiest lain untuk melakukannya?


75
2018-04-12 19:50


asal


Jawaban:


Ada banyak pendekatan untuk ini.

Jika Anda menggunakan Ruby 2.3 atau lebih baru, Anda dapat menggunakannya menggali

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

Banyak orang menempel pada ruby ​​polos dan rantai && tes penjaga.

Anda bisa menggunakan stdlib Hash # fetch terlalu:

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

Beberapa seperti peranti ActiveSupport #mencoba metode.

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

Lainnya digunakan dan dan

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

Sebagian orang berpikir egocentric nils adalah ide yang baik (meskipun seseorang mungkin memburu Anda dan menyiksa Anda jika mereka menemukan Anda melakukan ini).

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

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

Anda bisa menggunakannya Enumerable # reduce (atau alias menyuntikkan).

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

Atau mungkin memperpanjang Hash atau hanya objek hash target Anda dengan metode pencarian bertingkat

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, dan bagaimana kita bisa melupakan mungkin monad?

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

148
2018-04-12 20:36



Anda juga bisa menggunakan Objek # andand.

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

6
2018-04-12 20:24



Kondisi my_hash['key1'] && my_hash['key1']['key2'] tidak terasa KERING.

Alternatif:

1) autovivification sihir. Dari pos itu:

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

Kemudian, dengan contoh Anda:

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

Ini mirip dengan pendekatan Hash.fetch di mana keduanya beroperasi dengan hash baru sebagai nilai default, tetapi ini memindahkan detail ke waktu pembuatan. Harus diakui, ini sedikit curang: tidak akan pernah mengembalikan 'nihil' hanya hash kosong, yang dibuat dengan cepat. Bergantung pada kasus penggunaan Anda, ini bisa menjadi sia-sia.

2) Abstrak menghilangkan struktur data dengan mekanisme pencariannya, dan menangani kasus yang tidak ditemukan di belakang layar. Contoh sederhana:

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) Jika Anda merasa monadik Anda dapat melihat ini, Mungkin


5
2018-04-12 19:58