Reputation: 121
I'm trying to dynamically generate a case statement based on an array of values. For example let's say I have an array of ranges
[1..3,4..6,7..20,21..38]
and I want to write a dynamic case statement that returns the first number of whatever range
case n
ranges.each do |r|
when r
r.first
end
end
Is this possible, or will I have to find another way to do it (my actual code is more complex)?
Upvotes: 1
Views: 520
Reputation: 21160
In addition to #detect (or #find) with #include? from Jagdeep Singhs answer you can also use the case equality operator (Range#===
). This operator is used by the case statement to compare the input value with the scenario's you're providing.
ranges.find { |range| range === n }.first
Keep in mind both #detect and #find return nil
if no value can be found. This means you might want to use the safe navigation operator (}&.first
) to prevent a no method exception of #first on nil
if the value can't be found.
Upvotes: 1
Reputation: 11203
My two cents..
ranges = [1..3,4..6,7..20,21..38]
num = 15
ranges.bsearch { |range| range.member? num }.begin
Upvotes: 0
Reputation: 6041
Well, this works, but is kind of pointless and thread unsafe:
def get_range(n)
ranges = [1..3,4..6,7..20,21..38]
case n
when 3
# special case
199
when ->(x) { @_get_range = ranges.find { |r| r.cover?(x) } }
@_get_range.first
else
0
end
ensure
remove_instance_variable(:@_get_range) if instance_variable_defined?(:@_get_range)
end
get_range(3) # => 199
get_range(5) # => 4
get_range(50) # => 0
You could just do:
ranges.find { |r| r.cover?(n) }&.first || 0
Upvotes: 0
Reputation: 121020
Just out of curiosity:
number = 5
instance_eval [
"case number",
*ranges.map { |r| "when #{r} then (#{r}).first" },
"end"
].join($/)
#⇒ 4
Upvotes: 3
Reputation: 4920
If i get your question right, then you can forget case
statement and do it using detect:
ary = [1..3, 4..6, 7..20, 21..38]
num = 15 # say
ary.detect { |sub_ary| sub_ary.include?(num) }
=> 7..20
ary.detect { |sub_ary| sub_ary.include?(num) }.first # call `first` on result of above, which is a range, to get the first element.
=> 7
Upvotes: 4