2011-11-29 24 views
13

C'è una mano corta o una buona pratica per assegnare le cose a un hash quando sono nul in ruby? Ad esempio, il mio problema è che sto usando un altro hash per costruire questo e se qualcosa in esso è nullo, assegna nulla a quella chiave, piuttosto che lasciarla sola. Capisco perché questo accade così la mia soluzione era:Non assegnare valori nulli a un hash

hash1[:key] = hash2[:key] unless hash2[:key].nil? 

Perché non posso avere un valore nel ha dove la chiave in realtà punta a zero. (Preferirei avere un hash vuoto di uno che ha {: chiave => nil}, che non può accadere)

La mia domanda sarebbe: c'è un modo migliore per farlo? Non voglio fare un delete_if alla fine dei compiti.

+1

La soluzione sembra a posto per me. Se hai dato l'intero ciclo come esempio, ci sono molti buoni modi per farlo diventare una linea, ne sono sicuro. –

risposta

16

un po 'di hash1 più breve se si nega la "a meno che" l'istruzione

hash1[:key] = hash2[:key] if hash2[:key] # same as if ! hash2[:key].nil? 

si potrebbe anche fare il confronto in una dichiarazione & & come suggerito in altre risposte di Michael o Marc-Andre

Spetta a te, quello che senti è più leggibile per te. In base alla progettazione, in Ruby esistono sempre diversi modi per risolvere un problema.

Si potrebbe anche modificare il hash2:

hash1 = hash2.reject{|k,v| v.nil?} 

hash2.reject!{|k,v| v.nil?} # even shorter, if in-place editing of hash2 

questo sarebbe di rimuovere le coppie chiave/valore: (! A posto, se si utilizza rifiutare) key => nil da hash2

+0

> false.nil? => false – nroose

2

Che ne dici di qualcosa del genere?

hash2.each_pair do |key, value| 
    next if value.nil? 
    hash1[key] = value 
end 

Se si sta facendo un solo incarico, questo potrebbe radere alcuni caratteri:

hash2[:key] && hash1[:key] = hash2[:key] 

Il mio primo esempio potrebbe anche essere rasata un po 'più:

hash2.each_pair{ |k,v| v && hash1[k] = v } 

penso il primo è il più facile da leggere/capire. Inoltre, gli esempi 2 e 3 salteranno qualsiasi cosa che valuti false (nil o false). Questo ultimo esempio è una linea e non salterà false valori:

hash2.each_pair{ |k,v| v.nil? || hash1[k] = v } 
3

Non sono sicuro se questo è davvero niente di meglio, ma

hash2[:key] && hash[:key] = hash2[:key] 

potrebbe funzionare. Si noti che questo sarebbe comportata allo stesso modo per false e nil, se questo non è ciò che si vuole

!hash2[:key].nil? && hash[:key] = hash2[:key] 

sarebbe meglio. Tutto ciò presupponendo che :key sia un valore arbitrario di cui non si può avere il controllo.

+0

+1 è corretto. Ma fondamentalmente fa la stessa cosa della soluzione di Red – Tilo

+0

Ovviamente fa lo stesso. Dal modo in cui ho capito la domanda, non era contento dello stile del codice, non della funzionalità. –

3

Credo che la procedura migliore sia copiare il valore nil sull'hash. Se si passa un'opzione :foo => nil, può significare qualcosa e deve sovrascrivere un valore predefinito :foo di 42, ad esempio. Questo rende anche più facile avere opzioni che dovrebbe Default per true, anche se si dovrebbe usare fetch in quei casi:

opt = hash.fetch(:do_cool_treatment, true) # => will be true if key is not present 

Ci sono molti modi per copiare i valori, tra cui nil o false.

Per un solo tasto, è possibile utilizzare has_key? invece della ricerca:

hash1[:key] = hash2[:key] if hash2.has_key? :key 

Per le chiavi tutti (o molti), utilizzare merge!:

hash1.merge!(hash2) 

Se si desidera solo fare questo per un paio di chiavi di hash2, è possibile suddividerlo:

hash1.merge!(hash2.slice(:key, ...)) 
+0

Se 'hash2' ha chiavi con valori' nil', 'unione!' Non funzionerà come 'hash1' avrebbe le stesse chiavi e i loro valori' nil' ... esattamente ciò che l'OP voleva evitare. –

+0

l'unione ha lo svantaggio di copiare una chiave: key => nil coppia chiave/valore su hash1 se esiste in hash2. Penso che l'OP non lo volesse. – Tilo

+0

@Tilo: modificato per riflettere la mia opinione che è meglio copiare valori 'nil'. –

1

OK, quindi se l'unione non funziona perché si vuole un maggiore controllo:

hash1[:key] = hash2.fetch(:key, hash1[:key]) 

Questo sarà impostato hash1 di: chiave per essere hash2, a meno che non esiste. In tal caso, verrà utilizzato il valore predefinito (2 ° argomento per andare a prendere), che è la chiave

+0

non funzionerebbe se: la chiave non è già in hash1 (lato destro) –

+0

@RickR Hai ragione ... doh! ... dovrebbe anche andare sul lato destro: 'hash1 [: key] = hash2.fetch (: chiave, hash1.fetch (: key, nil))' ...e questo sembra un po 'brutto –

3

Mi piace questo il best, loop e condizionale sovrascrivono tutto in una riga!

h1 = {:foo => 'foo', :bar => 'bar'} 
h2 = {:foo => 'oof', :bar => nil} 

h1.merge!(h2) { |key, old_val, new_val| new_val.nil? ? old_val : new_val } 

#=> {:foo => 'oof', :bar => 'bar'} 

Questo sostituirà ogni valore in h1 con il valore di h2 dove le chiavi sono gli stessi e il valore H2 non è pari a zero.

+0

Funzionerà quando 'h1' inizia vuoto? – millimoose

+0

sì e no:/Se si ha un valore nullo in h2 e h1 è vuoto, imposterà il valore in h1 su zero. – Tilo

0

aggiungere questo alla tua initializers hash.rb

class Hash 
    def set_safe(key,val) 
    if val && key 
     self[key] = val 
    end 
    end 
end 

uso

hash = {} 
hash.set_safe 'key', value_or_nil