Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the shortest/most idiomatic way of determining whether a variable is any one of a list of values?

Tags:

syntax

ruby

This seems a ridiculously simple question to be asking, but what's the shortest/most idiomatic way of rewriting this in Ruby?

if variable == :a or variable == :b or variable == :c or variable == :d # etc.

I saw this solution:

if [:a, :b, :c, :d].include? variable

but this isn't always functionally equivalent - I believe Array#include? actually looks to see if the variable object is contained in the list; it doesn't take into account that the object may implement its own equality test with def ==(other).

As observed by helpful commentators below, that explanation isn't correct. include? does use == but it uses the == method of the items in the array. In my example, it's the symbols, rather than the == method of the variable. That explains why it's not equivalent to my first code example.

(Take, for example, Rails' Mime::Type implementation: request.format == :html may return true, but [:html].include?(request.format) will return false, as request.format is an instance of Mime::Type, not a symbol.)

The best I have so far is:

if [:a, :b, :c, :d].select {|f| variable == f}.any?

but it seems somewhat cumbersome to me. Does anyone have better suggestions?

like image 482
NeilS Avatar asked Sep 28 '09 13:09

NeilS


2 Answers

Actually, #include? does use ==. The problem arises from the fact that if you do

[:a].include? foo

it checks :a == foo, not foo == :a. That is, it uses the == method defined on the objects in the Array, not the variable. Therefore you can use it so long as you make sure the objects in the array have a proper == method.

In cases where that won't work, you can simplify your statement by removing the select statement and passing the block directly to any:

if [:a, :b, :c, :d].any? {|f| variable == f}
like image 128
Pesto Avatar answered Oct 15 '22 09:10

Pesto


It looks like Array#include? does use ==:

>> class AString < String
>>   def ==(other)
>>     self[0] == other[0]
>>   end
>> end

>> asdf = AString.new "asdfg"
=> "asdfg"
>> b = AString.new 'aer'
=> "aer"

>> asdf == b
=> true

>> [asdf].include? b
=> true

The Array#include? documentation also explains this.

like image 3
zgchurch Avatar answered Oct 15 '22 10:10

zgchurch