Reputation: 147
For instance:
options = { fight: true,
use_item: false,
run_away: false,
save_game: false }
I want a boolean expression that evaluates to true
iff only :fight
is true
, and the rest are false
(as illustrated above).
I can hack this together, but I'm trying to train myself to write more elegant ruby. Thanks!
EDIT: The hack being:
(options[:fight] == true && options.delete(:fight).values.all {|x| !x})
Upvotes: 5
Views: 1106
Reputation: 2460
If you control the content of the hash, and it is relatively small, I would use something like this in a private method as a generic solution.
def exclusively_true?(hash, key)
return false unless hash.delete(key) == true
!hash.has_value? true
end
require 'test/unit'
class TestExclusive < Test::Unit::TestCase
def setup
@test_hash = {foo: true, bar: false, hoge: false}
end
def test_exclusive
assert_equal(true, exclusively_true?(@test_hash, :foo))
end
def test_inexclusive
@test_hash[:bar] = true
assert_equal(false, exclusively_true?(@test_hash, :foo))
end
end
require 'benchmark'
h = {foo: true}
999.times {|i| h["a#{i}"] = false}
Benchmark.bmbm(30) do |x|
x.report('exclusively_true') do
1000.times do
exclusively_true?(h, :foo)
end
end
end
Contrived benchmarks: (OS X 10.8.3 / 3 GHz / 8 GB)
ruby -v: ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin12.3.0]
Rehearsal ------------------------------------------------------------------
exclusively_true 0.000000 0.000000 0.000000 ( 0.000412)
--------------------------------------------------------- total: 0.000000sec
user system total real
exclusively_true 0.000000 0.000000 0.000000 ( 0.000331)
Upvotes: 0
Reputation: 118271
options.find_all{|k,v| v } == [[:fight, true]]
or
options.values.count(true) == 1 && options[:fight]
Upvotes: 1
Reputation: 7530
Inspired by Vitaliy's answer:
options[:flight] && options.values.one?
Upvotes: 8
Reputation: 13574
How about:
options.all? {|k,v| k == :fight ? v : !v}
For a more general approach:
def is_action?(options, action)
options.all? {|k,v| k == action ? v : !v}
end
is_action? options, :fight
# => true
Upvotes: 1
Reputation: 6871
This one is independent of number of key/elems in the hash.
options[:fight] && options.find_all{|arr| !arr[1]}.size == options.size-1
Also Just a tip, in ruby, you never need to write something like:
options[:fight] == true
Upvotes: 1
Reputation: 1491
I think your hack is not bad. It can be simplified a little bit though:
options.delete(:flight) && options.values.none?
Upvotes: 2
Reputation: 36502
Assuming all values are strictly boolean, it's as simple as:
options == {fight: true, use_item: false, run_away: false, save_game: false}
See documentation for the == method
Upvotes: 9