2011-04-21 18 views
35

¿Hay alguna manera agradable (una línea) de escribir un hash en rubí con alguna entrada solo allí si se cumple una condición? Pensé enClave/valor condicional en un hash de ruby ​​

{:a => 'a', :b => ('b' if condition)} 

pero que deja :b == nil si no se cumple la condición. Me doy cuenta de que esto podría hacerse fácilmente en dos líneas más o menos, pero sería mucho mejor en una línea (por ejemplo, al pasar el hash a una función).

¿Me falta (todavía) otra de las increíbles características de ruby ​​aquí? ;)

+0

¿Qué desea para 'hash [: b]' cuando la condición no se cumple? En hash ordinario, obtendrá 'nil' de todos modos si no hay una clave coincidente. – sawa

+3

Si itera sobre el hash, entonces verá ': b' establecido incluso si' condition' es falso. –

Respuesta

36

Primero puede crear el hash con la tecla => nil para cuando la condición no se cumple, y luego eliminar aquellos pares donde el valor es nulo. Por ejemplo:

{ :a => 'a', :b => ('b' if cond) }.delete_if{ |k,v| v.nil? } 

rendimientos, para cond == true:

{:b=>"b", :a=>"a"} 

y para cond == false

{:a=>"a"} 

ACTUALIZACIÓN

Esto es equivalente - una un poco más conciso y en rubí notación 1.9.3:

{ a: 'a', b: ('b' if cond) }.reject{ |k,v| v.nil? } 
+1

Puede acortar '(cond? 'B': nil)' a '('b' if cond)'. Idea interesante. – sawa

+1

Jaja, sigan viniendo :) Buena captura, editada en consecuencia. – Thilo

+0

Si pongo el '.delete_if {| k, v | v.nil? } 'en una función separada, es incluso más corto. ¡Gracias! –

19

Los interesados ​​en conocer otras respuestas, pero esto es lo mejor que se me ocurre de una sola línea (también estoy muy malos en una sola línea: P)

{:a => 'a'}.merge(condition ? {:b => 'b'} : {}) 
+2

Hmm, buena idea, pero podría ser bastante feo con más de una entrada condicional ... –

+0

Tienes razón. No creo que encuentres un buen trazador de líneas para esto. Si es lo suficientemente complejo como para que sea tan feo, es probablemente lo suficientemente complejo como para construir el hash antes de tiempo. Solo mi 2 ¢. :) –

+1

Creo que esto es mejor que la respuesta aceptada, ya que no interferirá con ningún valor hash intencionalmente nulo. –

2
Hash[:a, 'a', *([:b, 'b'] if condition1), *([:c, 'c'] if condition2)] 

Esto se basa en el hecho de que *nil se expande a vacío en ruby ​​1.9. En Ruby 1.8, puede que tenga que hacer:

Hash[:a, 'a', *(condition1 ? [:b, 'b'] : []), *(condition2 ? [:c, 'c'] : [])] 

o

Hash[:a, 'a', *([:b, 'b'] if condition1).to_a, *([:c, 'c'] if condition2).to_a] 
+0

Genial, me gusta eso! ¿Alguna forma de escribir esto un poco más natural/más simple? –

+0

Si desea coherencia, puede agregar algunas cosas adicionales: 'Hash [* ([: a, 'a'] si es verdadero), * ([: b, 'b'] si condición1), * ([: c, 'c'] if condition2)] 'pero esto va en contra de la simplicidad. – sawa

+0

Eso no es lo que quise decir: D Solo estoy pensando si hay alguna manera de hacer esto con una notación '=>' más parecida a un hash, pero no puedo pensar en nada. –

1

Si tiene varias condiciones y la lógica que otros tendrán que comprender más tarde, entonces le sugiero que esto no es un buen candidato para una 1 revestimiento. Tendría más sentido crear correctamente su hash en función de la lógica requerida.

-4
eval("{:a => 'a' #{', :b => \'b\'' if condition }}") 

o incluso

eval("{#{[":a => 'a'", (":b=>'b'" if ax)].compact.join(',')}}") 

para añadir más condiciones simples

+2

noooo, por favor nunca use eval para algo como esto. – dalyons

+0

nunca use eval para algo;) – hypee

0
hash, hash_new = {:a => ['a', true], :b => ['b', false]}, {} 
hash.each_pair{|k,v| hash_new[k] = v[1] ? v : nil } 
puts hash_new 
1

Ésta es agradable para múltiples condicionales.

(
    hash = {:a => 'a'}.tap {|h| 
    h.store(*[(:b if condition_b), 'b']) 
    h.store(*[(:c if condition_c), 'c']) 
    } 
).delete(nil) 

Tenga en cuenta que elegí nil como la clave "basura", que se elimina cuando haya terminado. Si alguna vez tiene que almacenar un valor real con una clave nula, basta con cambiar las condicionales de la tienda para algo como:

(condition_b ? :b : garbage_key) 

a continuación, eliminar (garbage_key) al final.

Esta solución también mantendrá intactos los valores nulos existentes, p. si tuvieras: a => nil en el hash original, no será eliminado.

42

De Ruby 1.9+, si quieres construir un hash basado en condicionales puedes usar tap, que es mi nueva cosa favorita. Esto rompe en varias líneas, pero en mi humilde opinión es más legible:

{}.tap do |my_hash| 
    my_hash[:a] = 'a' 
    my_hash[:b] = 'b' if condition 
end 
+0

Pongo cualquier valor no condicional en el hash de inicio. Pero sí, esta es la forma en que lo haría, funciona incluso cuando quieres nils como valores hash, y sigue siendo muy legible, a diferencia de algunas de las soluciones publicadas. –

+0

Esta debería ser la respuesta correcta. Aquí está mi versión de una sola línea: '{a:" animal "}. Toque {| hash | hash [: b] = 'banana' si es verdadero} ' – TGPrankster

9

En Los Carriles:

{a: 'asd', b: nil}.compact 
=> {:a=>"asd"} 
+0

¡La solución funciona genial! – Abhinay

0

Mi solución de una sola línea:

{:a => 'a'}.tap { |h| h.merge!(:b => 'b') if condition } 
1

en Ruby 2.0 hay un doble splat operador (**) para hashes (y parámetros de palabra clave) por analogía al operador splat anterior (*) para matrices (y parámetros posicionales). Así que se podría decir:

{a: 'b', **(condition ? {b: 'b'} : {})} 
3

Hay una gran cantidad de soluciones inteligentes de aquí, pero la OMI el enfoque más simple y por lo tanto mejor es

hash = { a: 'a', b: 'b' } 
hash[:c] = 'c' if condition 

Va en contra de la solicitud de la OP de hacerlo en dos líneas, pero realmente también lo hacen las otras respuestas que solo parecen ser de una sola línea. Reconozcámoslo, esta es la solución más trivial y es fácil de leer.