2009-04-02 20 views
62

Sto cercando di capire come posso filtrare chiave e coppie di valori da un filtro in un altroRubino Hash Whitelist filtro

Per esempio io voglio prendere questo hash

x = { "one" => "one", "two" => "two", "three" => "three"} 

y = x.some_function 

y == { "one" => "one", "two" => "two"} 

Grazie per il vostro aiuto

EDIT: dovrei probabilmente menzionare che in questo esempio, voglio che si comporti come un filtro di lista bianca. Cioè, so quello che voglio, non quello che non voglio.

+0

suona come un duplicato di questo: http: // StackOverflow.it/questions/7430343/ruby-easiest-way-to-filter-hash-keys/# answer-30551883 – metakungfu

risposta

48

Forse questo è quello che vuoi.

wanted_keys = %w[one two] 
x = { "one" => "one", "two" => "two", "three" => "three"} 
x.select { |key,_| wanted_keys.include? key } 

La miscela di enumerazione inclusa per es. Array e Hash offrono molti metodi utili come select/reject/each/etc .. Suggerisco di dare un'occhiata alla documentazione per esso con ri Enumerable.

+0

ma il metodo select restituisce una matrice. In questo caso ho bisogno di un hash – stellard

+0

Ah, scusa, ho perso quel pezzetto. – sris

+0

Questo funziona in 1.9.2 ora, ma non ancora in 1.8.7 – stellard

48

Si può semplicemente usare la funzione di rifiuto hash incorporata.

x = { "one" => "one", "two" => "two", "three" => "three"} 
y = x.reject {|key,value| key == "three" } 
y == { "one" => "one", "two" => "two"} 

Si può mettere quello che vuoi logica nel respingere, e se il blocco restituisce true salterà quella chiave, il valore nella nuova hash.

+1

5 secondi indietro sulla digitazione ... Sapevo che avrei dovuto postare prima del test per assicurarmi di non aver fatto un errore di battitura. –

+0

Puoi usare 'reject!' Piuttosto che impostare un'altra variabile –

+2

@Radar I modificatori distruttivi possono causare problemi, se per esempio l'hash viene passato come argomento a un metodo e il chiamante non si aspetta che l'hash venga modificato da quel metodo. È meglio essere abituati a fare aggiornamenti non distruttivi e, se necessario, utilizzare solo operatori distruttivi. –

7
y = x.reject {|k,v| k == "three"} 
6

Usando una combinazione di risposte di tutti sono venuto su con questa soluzione:

wanted_keys = %w[one two] 
x = { "one" => "one", "two" => "two", "three" => "three"} 
x.reject { |key,_| !wanted_keys.include? key } 
=>{ "one" => "one", "two" => "two"} 

Grazie per il vostro aiuto ragazzi!

EDIT:

Le opere di cui sopra in 1.8.7+

le seguenti opere in 1.9+:

x.select {| chiave, _ | wanted_keys.include? chiave} biblioteca ActiveSupport

+1

Perché il doppio negativo? 'x.select {| chiave, _ | wanted_keys.include? tasto} '? Questo restituisce un hash per me –

+0

Dipende dalla versione di ruby ​​che stai utilizzando. Funziona in 1.9+ ma non in 1.8.7. Modificherò la risposta – stellard

+0

in downvote per aver accettato la tua copia della risposta di @ sris! Il tuo commento su quella risposta è sufficiente per sottolineare la differenza. – RobinGower

96

Rails' dà anche fetta e fatta eccezione per trattare con l'hash su un livello chiave:

y = x.slice("one", "two") # => { "one" => "one", "two" => "two" } 
y = x.except("three")  # => { "one" => "one", "two" => "two" } 
x.slice!("one", "two") # x is now { "one" => "one", "two" => "two" } 

questi sono abbastanza bello, e io uso loro tutto il tempo.

+0

E se x è una sottoclasse ActiveRecord :: Base, puoi fare 'y = x.attributes.slice *% w (uno due tre)'. –

+1

Se hai familiarità con ['_.pick' in Underscore.js] (http://underscorejs.org/#pick), questa è la stessa idea. (Sono venuto qui a cercarlo!) –

+0

E 'ActiveSupport' è piuttosto leggero, è usato da molte gemme di non-Rails. – skalee

6

Migliorare una risposta bit @scottd, se si utilizzano i binari e si dispone di un elenco di ciò che è necessario, è possibile espandere l'elenco come parametri dalla sezione. Per esempio

hash = { "one" => "one", "two" => "two", "three" => "three"} 
keys_whitelist = %W(one two) 
hash.slice(*keys_whitelist) 

E senza rotaie, per qualsiasi versione rubino, è possibile effettuare le seguenti operazioni:

hash = { "one" => "one", "two" => "two", "three" => "three"} 
keys_whitelist = %W(one two) 
Hash[hash.find_all{|k,v| keys_whitelist.include?(k)}] 
+0

Nota che non è necessario utilizzare * Rails *, è sufficiente caricare Active Support. Aggiungi 'active_support' al tuo Gemfile e 'richiedi" active_support/core_ext/hash/slice "'. – GMA

+0

'find_all' è un alias per' select' quindi questo è fondamentalmente lo stesso delle prime due risposte :-) – AlexChaffee

0

userei un lambda per filtrare. Ciò ti consentirà entrambi di scrivere una logica di filtraggio complessa, &, semplificando la verifica. Il fatto che la logica di filtraggio sia stata estratta permetterà di riutilizzarla in altri contesti.

es:

x = { "one" => "one", "two" => "two", "three" => "three"} 

matcher = ->(key,value) { 
    # FILTERING LOGIC HERE 
    !key[/three/] 
} 

x.select(&matcher) == { "one" => "one", "two" => "two"}