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.
build hash from conditional statements
merge
makes this very concise:
def create_hash(opt ={})
{
a: 0.40,
b: 0.30,
c: 0.05
}.merge(opt)
end
create_hash :a => 0.30
# => {:a=>0.3, :b=>0.3, :c=>0.05}
We use merge
in some of our in-house code to fine-tune configuration data. We have a global YAML file, then if we need to specify something different for a particular app or host, we have small YAML files containing just the hash data we need to modify. We merge the smaller hash with the global one and overwrite only the data that needs to change.
Looking at what you're doing:
hash = {:a => if opt[:a] ? a : 0.40; end},
:b => if opt[:b] ? b : 0.30; end,
:c => if opt[:c] ? c : 0.05; end}
There's a lot of confusion about how Ruby works. You'll immediately run into syntax errors:
opt = {}
hash = {:a => if opt[:a] ? a : 0.40; end},
:b => if opt[:b] ? b : 0.30; end,
:c => if opt[:c] ? c : 0.05; end}
# =>
# ~> -:3: syntax error, unexpected =>, expecting end-of-input
# ~> :b => if opt[:b] ? b : 0.30; end,
# ~> ^
That's because you can't:
- use a trailing conditional inside a hash assignment.
- end the first line with
},
which terminates the hash construction.
Cleaning up the indentation and removing the if
and end;...
:
opt = {}
hash = {
:a => opt[:a] ? a : 0.40,
:b => opt[:b] ? b : 0.30,
:c => opt[:c] ? c : 0.05
}
# => {:a=>0.4, :b=>0.3, :c=>0.05}
That'd work, however it's still got code smell because it's difficult to see what's really happening because of the ternary ("?:") statements. Ternary statements aren't inherently evil, but they can be harder to read, so use them sparingly, and always be aware that they can affect the clarity of the code.
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? }
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
Conditional map of Hash with join to concatenate a string
I believe this is an XY-question. I think you should do it this way:
aql_string = "FILTER " <<
conditions.map{|k, v| "user.#{k} == #{v.inspect}"}.join(" && ")
This will surround strings with double quotes instead of single quotes, but I believe that is not a problem.
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).
Printing Values in a Hash using the conditional operator for and Ruby's .each
You're asking Ruby to do way too much at once here:
for q, e in my_stocks.to_i < 500
That really doesn't make any sense. for
iterates over a container. to_i
returns an integer. x.to_i < n
returns a boolean. You can't iterate over a boolean. Plus, my_stocks
is a Hash and it doesn't do to_i
, so this whole line is not something Ruby can make sense of.
Instead think about the problem differently. The Ruby way is to break this down into steps. First, find all stocks over a particular value:
my_stocks.select do |_name, data|
data['price'] > 500
end.each do |name, data|
# ... print or whatever
end
Where this taps into Enumerable to help solve the problem in two stages. select
to filter, each
to iterate. In Ruby for
isn't really used, it's really never the best tool for the job.
The real power of Enumerable is that you can chain one operation right into the next. end.each
might look very odd to someone unfamiliar with Ruby, but that's really how Ruby handles complex operations with ease.
For example, to filter and sort by price (highest to lowest) is only a small modification:
my_stocks.select do |_name, data|
data['price'] > 500
end.sort_by do |_name, data|
-data['price']
end.each do |name, data|
# ... print or whatever
end
The -data['price']
part is an easy way of avoiding having to reverse the values later. Normally it sorts in increasing order, so inverting sorts in decreasing as the highest value becomes the lowest.
Related Topics
How to Access a Ruby Module Method
Anybody Tried the Crystal Programming Language (Machine-Code Compiled Ruby)
Restarting Unicorn with Usr2 Doesn't Seem to Reload Production.Rb Settings
How to Switch to Older Versions of the Ruby/Rails Environment
Regex with Named Capture Groups Getting All Matches in Ruby
Why and When to Use the Shell Instead of Ruby
Generate Ruby Classes from Xsd
Adding an Instance Variable to a Class in Ruby
Rails: How to Show User's "Last Seen At" Time
Neither Ruby and Nor Irb Can Load .Rb File in Current Directory
Rails: Your User Account Isn't Allowed to Install to the System Rubygems
Switching Between Web and Touch Interfaces on Facebook Login Using Omniauth and Rails 3
Ruby: Insert Spaces Every X Number of Characters
Help Refactoring This Nasty Ruby If/Else Statement
Difference Between Add_Dependency and Add_Runtime_Dependency