2009-04-02 17 views
62

Estoy tratando de averiguar cómo puedo filtrar los pares de clave y valor de un filtro en otraRubí Hash lista blanca del filtro

Por ejemplo, yo quiero tomar este hash

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

y = x.some_function 

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

Gracias por su ayuda

EDITAR: probablemente debería mencionar que en este ejemplo, quiero que se comporte como un filtro de lista blanca. Es decir, sé lo que quiero, no lo que no quiero.

+0

suena como un duplicado de éste: http: // stackoverflow.com/questions/7430343/ruby-easiest-way-to-filter-hash-keys/# answer-30551883 – metakungfu

Respuesta

48

Quizás esto es lo que quieres.

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

La mezcla de Enumerable que se incluye en p. Array and Hash proporciona una gran cantidad de métodos útiles, como select/reject/each/etc. Le sugiero que eche un vistazo a la documentación con Ri Enumerable.

+0

pero el método de selección devuelve una matriz. En este caso necesito un hash – stellard

+0

Ah, lo siento, me perdí ese bit. – sris

+0

Esto funciona en 1.9.2 ahora, pero aún no en 1.8.7 – stellard

48

Puede utilizar el rechazo de la función hash incorporada.

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

Usted puede poner lo lógica que desea en el rechazo, y si el bloque devuelve verdadero se saltará esa tecla, el valor en el nuevo hash.

+1

5 segundos detrás de la escritura ... Sabía que debería haber publicado antes de la prueba para asegurarme de que no hice un error. –

+0

Puede usar 'reject!' En lugar de establecer otra variable –

+2

@Radar Los modificadores destructivos pueden causar problemas si, por ejemplo, el hash se pasa como argumento a un método y el llamante no espera que el hash sea modificado por ese método. Lo mejor es tener el hábito de realizar actualizaciones no destructivas y solo usar operadores destructivos cuando sea necesario. –

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

Usando una combinación de las respuestas de todos los que han llegado con esta solución:

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

Gracias por su ayuda chicos!

EDIT:

Los trabajos anteriores en 1.8.7+

las siguientes obras: 1.9+ en

x.select {| llave, _ | wanted_keys.include? clave} Biblioteca ActiveSupport

+1

¿Por qué el doble negativo? 'x.select {| key, _ | wanted_keys.include? clave} '? Esto devuelve un hash para mí –

+0

Depende de la versión de ruby ​​que esté utilizando. Esto funciona en 1.9+ pero no en 1.8.7. Voy a editar la respuesta – stellard

+0

Downvote para aceptar tu propia copia de la respuesta de @sris! Su comentario sobre esa respuesta es suficiente para señalar la diferencia. – RobinGower

96

Rails también da que se mire y con excepción de tratar con el hash en un nivel clave:

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" } 

Estos son bastante agradable, y los uso todo el tiempo.

+0

Y si x es una subclase ActiveRecord :: Base, puede hacer 'y = x.attributes.slice *% w (one two three)'. –

+1

Si está familiarizado con ['_.pick' en Underscore.js] (http://underscorejs.org/#pick), esta es la misma idea. (¡Vine aquí buscando eso!) –

+0

Y 'ActiveSupport' es bastante ligero, es utilizado por muchas gemas que no son de Rails. – skalee

6

Mejorando un poco @scottd respuesta, si está utilizando los rieles y tiene una lista de lo que necesita, puede ampliar la lista como parámetros de división. Por ejemplo

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

Y sin raíles, para cualquier versión de rubí, puede hacer lo siguiente:

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

Tenga en cuenta que no necesita necesariamente utilizar * Rails *, solo puede cargar Active Support. Agrega 'active_support' a tu Gemfile y' require "active_support/core_ext/hash/slice" '. – GMA

+0

'find_all' es un alias para' select', por lo que básicamente es lo mismo que las primeras dos respuestas :-) – AlexChaffee

0

que haría uso de un lambda filtrar. Esto le permitirá escribir lógica compleja de filtrado & para que sea fácil probarlo. El hecho de que se extraiga la lógica de filtrado permitirá reutilizarlo en otros contextos.

ejemplo:

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

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

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