Conditional Key/Value in a Ruby Hash

Conditional key/value in a ruby hash

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

Ruby - how to add a conditional key to a hash?

Not the prettiest solution, but ** could be used:

{
key1: data.k1,
key2: data.k2,
**(@var == true ? { key3: data.k3 } : {}),
key4: data.k4,
}

Note that == true can be omitted if you just care whether @var is truthy.

Conditional inclusion of a key-value pair in a hash


def self.some_hash(some_key = nil)
{"foo" => "bar"}.merge(some_key ? {some_key => "yucky, long-winded syntax"} : {})
end

Or, if modifying the original hash,

def self.some_hash(some_key = nil)
{"foo" => "bar"}
.tap{|h| h.merge!(some_key => "yucky, long-winded syntax") if some_key}
end

Or, maybe you can do it in a way close to your original:

def self.some_hash(some_key = nil)
{"foo" => "bar"}
.tap{|h| h[some_key] = "yucky, long-winded syntax" if some_key}
end

Building a hash in a conditional way

I prefer tap, as I think it provides a cleaner solution than the ones described here by not requiring any hacky deleting of elements and by clearly defining the scope in which the hash is being built.

It also means you don't need to declare an unnecessary local variable, which I always hate.

In case you haven't come across it before, tap is very simple - it's a method on Object that accepts a block and always returns the object it was called on. So to build up a hash conditionally you could do this:

Hash.new.tap do |my_hash|
my_hash[:x] = 1 if condition_1
my_hash[:y] = 2 if condition_2
...
end

There are many interesting uses for tap, this is just one.

Conditionally set value of key in hash

You just need some brackets around the values:

def parse(issue)
{
#...
asignee_handle: (issue['assignee']['login'] if issue['assignee']),
#...
}
end

The value of the :asignee_handle key will now either be issue['assignee']['login'] or it will be nil (or you will get an error if issue['assignee'] isn’t a hash).

Ruby hash keys to array conditional on hash value


h.select { |_, v| v }.keys

Will do the same, but in more readable way.

Creating a key value pair based on a condition in a Ruby hash

You could use Hash#merge! method to merge unique key value pairs based on condition.

parameters= {
'SearchIndex' => params[:searchIndex],
'Keywords' => params[:search_term]
}

case params[:searchindex]
when 'All'
parameters.merge!({ :'ResponseGroup' => 'ItemAttributes' })
else
parameters.merge!({ :'ResponseGroup' => 'ItemAttributes,Images', :'Sort' => 'salesrank' })
end

For every other searchindex you would add a when... condition and merge! the required attributes.

ruby do statements if hash key satisfies condition

I will just show how this can be done quite economically in Ruby. Until recently, when wanting to modify keys of a hash one usually would do one of two things:

  • create a new empty hash and then add key/values pairs to the hash; or
  • convert the hash to an array a of two-element arrays (key-value pairs), modify the first element of each element of a (the key) and then convert a to the desired hash.

Recently (with MRI v2.4) the Ruby monks bestowed on us the handy methods Hash#transform_keys and Hash#transform_keys!. We can use the first of these profitably here. First we need a regular expression to match keys.

r = /
\A # match beginning of string
first_type_ # match string
(\p{Lower}+) # match 1+ lowercase letters in capture group 1
\z # match the end of the string
/x # free-spacing regex definition mode

Conventionally, this is written

r = /\Afirst_type_(\p{Lower}+)\z/

The use of free-spacing mode makes the regex self-documenting. We now apply the transform_keys method, together with the method String#sub and the regex just defined.

event = {"first_type_a"=>100, "first_type_b"=>false,
"second_type_a"=>"abc", "second_type_b"=>false}

event.transform_keys { |k| k.sub(r, "important.#{'\1'}_1") }
#=> {"important.a_1"=>100, "important.b_1"=>false,
# "second_type_a"=>"abc", "second_type_b"=>false}

In the regex the p{} construct expression \p{Lower} could be replaced with \p{L}, the POSIX bracket expression [[:lower:]] (both match Unicode letters) or [a-z], but the last has the disadvantage that it will not match letters with diacritical marks. That includes letters of words borrowed from other languages that are used in English text (such as rosé, the wine). Search Regexp for documentation of POSIX and \p{} expressions.

If "first_type_" could be followed by lowercase or uppercase letters use \p{Alpha}; if it could be followed by alphanumeric characters, use \p{Alnum}, and so on.

Conditional set value in a ruby hash

Basing on Dave comment:

  def stores_distances
site = Site.find params[ :id ]
distances = Store.current.map do |store|
{
name: store.name,
value: distance(site.address, store.address) || 'no precision address'
}
end

render json: {
distances: distances.to_json
}
end
def distance(site_address, store_address)
return nil unless store_address.latitude && store_address.longitude
Haversine.distance(
site_address.latitude,
site_address.longitude,
store_address.latitude,
store_address.longitude
).round(2)
end


Related Topics



Leave a reply



Submit