Clé / valeur conditionnelle dans un hash ruby

Existe-t-il une manière (une seule ligne) d’écrire un hash en ruby ​​avec une entrée uniquement si une condition est remplie? j’ai pensé à

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

Mais cela laisse :b == nil si la condition n’est pas remplie. Je me rends compte que cela pourrait se faire facilement en deux lignes ou plus, mais cela serait beaucoup plus agréable sur une seule ligne (par exemple, lors du passage du hachage à une fonction).

Est-ce que je manque (encore) une autre caractéristique étonnante de Ruby? 😉

Vous pouvez d’abord créer le hachage avec la clé => nil lorsque la condition n’est pas remplie, puis supprimer les paires dont la valeur est nulle. Par exemple:

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

rendements, pour cond == true:

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

et pour cond == false

 {:a=>"a"} 

METTRE À JOUR

Ceci est équivalent – un peu plus concis et en notation Ruby 1.9.3:

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

À partir de Ruby 1.9+, si vous voulez créer un hachage basé sur des conditions, vous pouvez utiliser le tap , ce qui est mon nouveau truc préféré. Cela le divise en plusieurs lignes mais est plus lisible à mon humble avis:

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

Intéressé à voir d’autres réponses, mais c’est le meilleur que je puisse imaginer pour un one-liner (je suis également notoirement mauvais à un seul interlocuteur: P)

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

> = Ruby 2.4:

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

Il y a beaucoup de solutions intelligentes ici, mais l’OMI est la plus simple et donc la meilleure approche est

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

Cela va à l’encontre de la demande du PO de le faire en deux lignes, mais en réalité, les autres réponses ne semblent être que des lignes à sens unique. Let’s face it, c’est la solution la plus sortingviale et facile à lire.

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

Cela repose sur le fait que *nil s’étend à la vacuité dans Ruby 1.9. Dans Ruby 1.8, vous devrez peut-être faire:

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

ou

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

Si vous avez plusieurs conditions et une logique que les autres devront comprendre plus tard, je pense que ce n’est pas un bon candidat pour une ligne. Il serait plus judicieux de créer correctement votre hachage en fonction de la logique requirejse.

Celui-ci est bien pour plusieurs conditions.

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

Notez que j’ai choisi la clé “garbage”, qui est supprimée lorsque vous avez terminé. Si vous avez besoin de stocker une valeur réelle avec une clé nil, remplacez simplement les conditions du magasin par:

 (condition_b ? :b : garbage_key) 

puis supprimez (garbage_key) à la fin.

Cette solution gardera également les valeurs nil existantes intactes, par exemple si vous avez: a => nil dans le hash original, il ne sera pas supprimé.

Dans Ruby 2.0, il existe un opérateur double splat ( ** ) pour les hachages (et les parameters de mot-clé) par analogie avec l’ancien opérateur de splat ( * ) pour les tableaux (et les parameters de position). Donc, vous pourriez dire:

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

Ma solution one-liner:

 {:a => 'a'}.tap { |h| h.merge!(:b => 'b') if condition } 
 eval("{:a => 'a' #{', :b => \'b\'' if condition }}") 

ou même

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

pour des conditions d’ajout plus simples