Conditional key/value in a ruby hash

Ruby

Ruby Problem Overview


Is there a nice (one line) way of writing a hash in ruby with some entry only there if a condition is fulfilled? I thought of

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

But that leaves :b == nil if the condition is not fulfilled. I realize this could be done easily in two lines or so, but it would be much nicer in one line (e.g. when passing the hash to a function).

Am I missing (yet) another one of ruby's amazing features here? ;)

Ruby Solutions


Solution 1 - Ruby

UPDATE Ruby 2.4+

Since ruby 2.4.0, you can use the compact method:

{ a: 'a', b: ('b' if cond) }.compact

Original answer (Ruby 1.9.2)

You could first create the hash with key => nil for when the condition is not met, and then delete those pairs where the value is nil. For example:

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

yields, for cond == true:

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

and for cond == false

{:a=>"a"} 

UPDATE for ruby 1.9.3

This is equivalent - a bit more concise and in ruby 1.9.3 notation:

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

Solution 2 - Ruby

From Ruby 1.9+, if you want to build a hash based on conditionals you can use tap, which is my new favourite thing. This breaks it onto multiple lines but is more readable IMHO:

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

Solution 3 - Ruby

>= Ruby 2.4:

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

Solution 4 - Ruby

Interested in seeing other answers, but this is the best I can think up of for a one-liner (I'm also notoriously bad at one-liners :P)

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

Solution 5 - Ruby

There's a lot of clever solutions in here, but IMO the simplest and therefore best approach is

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

It goes against the OP's request of doing it in two lines, but really so do the other answers that only appear to be one-liners. Let's face it, this is the most trivial solution and it's easy to read.

Solution 6 - Ruby

In Ruby 2.0 there is a double-splat operator (**) for hashes (and keyword parameters) by analogy to the old splat operator (*) for arrays (and positional parameters). So you could say:

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

Solution 7 - Ruby

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

This relies on the fact that *nil expands to vacuity in ruby 1.9. In ruby 1.8, you might need to do:

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

or

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

Solution 8 - Ruby

If you have multiple conditions and logic that others will need to understand later then I suggest this is not a good candidate for a 1 liner. It would make more sense to properly create your hash based on the required logic.

Solution 9 - Ruby

This one is nice for multiple conditionals.

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

Note that I chose nil as the "garbage" key, which gets deleted when you're done. If you ever need to store a real value with a nil key, just change the store conditionals to something like:

(condition_b ? :b : garbage_key)

then delete(garbage_key) at the end.

This solution will also keep existing nil values intact, e.g. if you had :a => nil in the original hash, it won't be deleted.

Solution 10 - Ruby

My one-liner solution:

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

Solution 11 - Ruby

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

Solution 12 - Ruby

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

or even

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

for more simple add conditions

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionlucas clementeView Question on Stackoverflow
Solution 1 - RubyThiloView Answer on Stackoverflow
Solution 2 - RubyRussellView Answer on Stackoverflow
Solution 3 - RubysitesView Answer on Stackoverflow
Solution 4 - RubyMichelle TilleyView Answer on Stackoverflow
Solution 5 - RubyDennisView Answer on Stackoverflow
Solution 6 - RubyPaul A JungwirthView Answer on Stackoverflow
Solution 7 - RubysawaView Answer on Stackoverflow
Solution 8 - RubyWesView Answer on Stackoverflow
Solution 9 - RubyKelvinView Answer on Stackoverflow
Solution 10 - RubyArturo HerreroView Answer on Stackoverflow
Solution 11 - RubyAnatolyView Answer on Stackoverflow
Solution 12 - RubySectorView Answer on Stackoverflow