2015-12-18 1 views
21

Ruby 2.3 introduce un nuovo metodo su Array e Hash chiamato . Gli esempi che ho visto in post del blog circa la nuova release sono artificiosa e complicata:Come utilizzare "array # dig" e "Hash # dig" introdotto in Ruby 2.3?

# Hash#dig 
user = { 
    user: { 
    address: { 
     street1: '123 Main street' 
    } 
    } 
} 

user.dig(:user, :address, :street1) # => '123 Main street' 

# Array#dig 
results = [[[1, 2, 3]]] 
results.dig(0, 0, 0) # => 1 

non sto usando gli array piatto triplo nidificati. Qual è un esempio realistico di come sarebbe utile?

UPDATE

Si scopre questi metodi risolvono una delle domande più comuni di Ruby-poste. Le domande sotto hanno qualcosa come 20 duplicati, che sono tutti risolti utilizzando :

How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?

Ruby Style: How to check whether a nested hash element exists

+1

Hai solo analizzato alcune JSON è un esempio molto realistico ... – ndn

+0

@ ndn L'utente normale 'utente [: utente] [: indirizzo] [: via1]' ha un numero di caratteri inferiore a 'user.dig (: user,: address,: street1)' e dà lo stesso risultato. –

+7

@JesseSielaff Non sono la stessa cosa. '[] [] []' fallirà con un errore se nessuna delle chiavi non esiste. 'dig' non fallirà, restituirà' nil'. – meagar

risposta

41

Nel nostro caso, NoMethodError s dovuti a nil riferimenti sono di gran lunga gli errori più comuni che vediamo nei nostri ambienti di produzione.

Il nuovo Hash#dig consente di omettere i controlli nil durante l'accesso agli elementi nidificati. Poiché gli hash vengono utilizzati al meglio quando la struttura dei dati è sconosciuta o volatile, avere un supporto ufficiale per questo ha molto senso.

Prendiamo il tuo esempio. Il seguente:

user.dig(:user, :address, :street1) 

È non equivalente a:

user[:user][:address][:street1] 

Nel caso in cui user[:user] o user[:user][:address] è nil, questo si tradurrà in un errore di runtime.

Piuttosto, è equivalente a quanto segue, che è il linguaggio corrente:

user[:user] && user[:user][:address] && user[:user][:address][:street1] 

Nota come è banale per passare un elenco di simboli che è stato creato altrove in Hash#dig, mentre non è molto semplice ricreare l'ultimo costrutto da una tale lista. Hash#dig consente di eseguire facilmente l'accesso dinamico senza doversi preoccupare dei riferimenti nil.

Chiaramente Hash#dig è anche molto più breve.


Un punto importante prendere nota è che Hash#dig si restituisce nil se uno qualsiasi dei tasti risultano essere, che può portare alla stessa classe di errori un passo lungo la linea, in modo che possa essere buona idea per fornire un valore ragionevole. (Questo modo di fornire un oggetto che risponde sempre ai metodi previsti è chiamato Null Object Pattern.)

Ancora una volta, nel tuo esempio, una stringa vuota o qualcosa di simile "N/A", a seconda di ciò che ha senso:

user.dig(:user, :address, :street1) || "" 
+0

Vorrei che ci fosse un "Hash # dig!" Che avrebbe fatto l'equivalente di più # fetch. – Dogweather

+0

@Dogweather: puoi provare a inviare una richiesta di funzione nel [issue tracker] (https://bugs.ruby-lang.org/projects/ruby-trunk) e vedere se qualcuno lo preleva. :-) – Drenmi

+0

Gli idiomi alternativi a volte visti sono 'arr.fetch (: user, {}). Fetch (: address, {}) [: street1]' e per Rails, 'arr [: user] .try (: [] ,: indirizzo) .try (: [],: street1) '. –

9

Un modo sarebbe in concomitanza con l'operatore splat lettura da qualche modello di documento sconosciuto.

some_json = JSON.parse('{"people": {"me": 6, ... } ...}') 
# => "{"people" => {"me" => 6, ... }, ... } 
a_bunch_of_args = response.data[:query] 
# => ["people", "me"] 
some_json.dig(*a_bunch_of_args) 
# => 6 
+2

Ah, è intelligente. Non l'avevo visto in nessun post di esempio. –