Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I have a value 'Dog' and an array ['Cat', 'Dog', 'Bird'].

How do I check if it exists in the array without looping through it? Is there a simple way of checking if the value exists, nothing more?

share|improve this question
1  
use the .include? method. It returns a boolean which is what you want. In your case just type: ['Cat', 'Dog', 'Bird'].include('Dog') and it should return the boolean true. – Jwan622 Feb 5 '16 at 3:20

21 Answers 21

up vote 1319 down vote accepted

You're looking for include?:

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
share|improve this answer
46  
Alternate syntax: %w(Cat Dog Bird).include? 'Dog' – scarver2 Dec 18 '12 at 22:04
114  
Sometimes I wish it was "contains" not include. I always get it mixed up with includes. – Henley Chiu Oct 9 '13 at 2:11
10  
Let me just note that internally, #include? still does perform looping. The coder is saved from writing the loop explicitly, though. I have added an answer that performs the task truly without looping. – Boris Stitnicky Dec 16 '13 at 3:49
5  
@HenleyChiu I which it was called [ 'Dog', 'Bird', 'Cat' ].has? 'Dog' – nus May 21 '14 at 23:01
1  
@AlfonsoVergara Yes, any solution for an array must do some sort of looping internally; there is no way to test for membership of an array without looping. If you don't want to do any looping even internally, you need to use a different data structure, such as a perfect hash table with fixed sized keys. Given that there's no way to test for membership in an array without looping internally, I interpreted the question to mean "without having to write the loop explicitly myself" – Brian Campbell Sep 10 '16 at 15:45

There is an in? method in ActiveSupport (part of Rails) since v3.1, as pointed out by @campaterson. So within Rails, or if you require 'active_support', you can write:

'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false

OTOH, there is no in operator or #in? method in Ruby itself, even though it has been proposed before, in particular by Yusuke Endoh a top notch member of ruby-core.

As pointed out by others, the reverse method include? exists, for all Enumerables including Array, Hash, Set, Range:

['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false

Note that if you have many values in your array, they will all be checked one after the other (i.e. O(n)), while that lookup for a hash will be constant time (i.e O(1)). So if you array is constant, for example, it is a good idea to use a Set instead. E.g:

require 'set'
ALLOWED_METHODS = Set[:to_s, :to_i, :upcase, :downcase
                       # etc
                     ]

def foo(what)
  raise "Not allowed" unless ALLOWED_METHODS.include?(what.to_sym)
  bar.send(what)
end

A quick test reveals that calling include? on a 10 element Set is about 3.5x faster than calling it on the equivalent Array (if the element is not found).

A final closing note: be wary when using include? on a Range, there are subtleties, so refer to the doc and compare with cover?...

share|improve this answer
    
While Ruby doesn't include #in? in it's core, if you are using Rails, it is available. api.rubyonrails.org/classes/Object.html#method-i-in-3F (I know this is a Ruby, not a Rails question, but it may help anyone looking to use #in? in Rails. Looks like it was added in Rails 3.1 apidock.com/rails/Object/in%3F – campeterson Aug 21 '13 at 17:34
5  
+1 for Set, oft-overlooked. – Jared Beck Oct 12 '14 at 3:49

Try

['Cat', 'Dog', 'Bird'].include?('Dog')
share|improve this answer
    
this is the older syntax, look ^^^ @brian's answer – jahrichie Feb 24 '14 at 15:30
48  
@jahrichie what exactly do you consider "older syntax" in this answer, the optional parentheses? – Dennis Jul 11 '14 at 22:50

Use Enumerable#include:

a = %w/Cat Dog Bird/

a.include? 'Dog'

Or, if a number of tests are done,1 you can get rid of the loop (that even include? has) and go from O(n) to O(1) with:

h = Hash[[a, a].transpose]
h['Dog']


1. I hope this is obvious but to head off objections: yes, for just a few lookups, the Hash[] and transpose ops dominate the profile and are each O(n) themselves.

share|improve this answer

If you want to check by a block, you could try any? or all?.

%w{ant bear cat}.any? {|word| word.length >= 3}   #=> true  
%w{ant bear cat}.any? {|word| word.length >= 4}   #=> true  
[ nil, true, 99 ].any?                            #=> true  

Details are here: http://ruby-doc.org/core-1.9.3/Enumerable.html
My inspiration come from here: http://stackoverflow.com/a/10342734/576497

share|improve this answer
1  
Very useful if you want check any/all of those string is included in another string/constant – thanikkal Jul 12 '12 at 12:40

Several answers suggest Array#include?, but there is one important caveat: Looking at the source, even Array#include? does perform looping:

rb_ary_includes(VALUE ary, VALUE item)
{
    long i;

    for (i=0; i<RARRAY_LEN(ary); i++) {
        if (rb_equal(RARRAY_AREF(ary, i), item)) {
            return Qtrue;
        }
    }
    return Qfalse;
}

The way to test the word presence without looping is by constructing a trie for your array. There are many trie implementations out there (google "ruby trie"). I will use rambling-trie in this example:

a = %w/cat dog bird/

require 'rambling-trie' # if necessary, gem install rambling-trie
trie = Rambling::Trie.create { |trie| a.each do |e| trie << e end }

And now we are ready to test the presence of various words in your array without looping over it, in O(log n) time, with same syntactic simplicity as Array#include?, using sublinear Trie#include?:

trie.include? 'bird' #=> true
trie.include? 'duck' #=> false
share|improve this answer
4  
a.each do ... end Umm... not sure how that's not a loop – Doorknob Dec 15 '13 at 0:57
20  
Note that this does actually include a loop; anything that's not O(1) includes some kind of loop. It just happens to be a loop over the characters of the input string. Also note than an answer already mentioned Set#include? for people who are concerned about efficiency; coupled with using symbols instead of strings, it can be O(1) average case (if you use strings, then just computing the hash is O(n) where n is the length of the string). Or if you want to use third party libraries, you can use a perfect hash which is O(1) worst case. – Brian Campbell Dec 16 '13 at 7:21
2  
AFAIK, Set uses hashes to index its members, so actually Set#include? should be of complexity O(1) for a well-distributed Set (more specifically O(input-size) for the hashing, and O(log(n/bucket-number)) for the searching) – Uri Agassi Feb 7 '14 at 20:40
8  
The cost of creating and maintaining the trie is just as much. If you are doing many search operations on the array, then the memory and time cost of populating a trie and maintaining it is worth it, but for single, or even hundreds or thousands of checks, O(n) is perfectly suitable. Another option that doesn't require adding dependencies would be to sort the array or maintain it in sorted order, in which case a binary search O(lg n) operation can be used to check inclusion. – speakingcode Feb 13 '14 at 22:48
1  
@speakingcode, you may be right from the pragmatic point of view. But the OP asks to "check if the value exists, nothing more, without looping". When I wrote this answer, there were many pragmatic solutions here, but none that would actually meet the asker's literal requirement. Your observation that BSTs are related to tries is correct, but for strings, trie is the right tool for the job, even Wikipedia knows that much. Complexity of constructing and maintaing a well-implemented trie is surprisinly favorable. – Boris Stitnicky Feb 14 '14 at 1:05

This is another way to do this: use the Array#index method.

It returns the index of the first occurrence of the element in the array.

example:

a = ['cat','dog','horse']
if a.index('dog')
    puts "dog exists in the array"
end

index() can also take a block

for example

a = ['cat','dog','horse']
puts a.index {|x| x.match /o/}

here, return the index of the first word in the array that containing letter 'o'.

share|improve this answer
1  
That's actually quite useful. – superluminary Apr 9 '14 at 12:52
    
index still iterates over the array, it just returns the value of the element. – the Tin Man Jan 20 '16 at 22:37

If you don't want to loop, there's no way to do it with Arrays. You should use a Set instead.

require 'set'
s = Set.new
100.times{|i| s << "foo#{i}"}
s.include?("foo99")
 => true
[1,2,3,4,5,6,7,8].to_set.include?(4) 
  => true

Sets work internally like hashes, so Ruby doesn't need to loop through the collection to find items, since as the name implies, it generates hashes of the keys and creates a memory map so that each hash point to a certain point in memory. The previous example done with a Hash:

fake_array = {}
100.times{|i| fake_array["foo#{i}"] = 1}
fake_array.has_key?("foo99")
  => true

The downside is that Sets and hash keys can only include unique items and if you add a lot of items, Ruby will have to rehash the whole thing after certain number of items to build a new map that suits a larger keyspace. For more about this, I recommend you watch MountainWest RubyConf 2014 - Big O in a Homemade Hash by Nathan Long

Here's a benchmark:

require 'benchmark'
require 'set'

array = []
set   = Set.new

10_000.times do |i|
  array << "foo#{i}"
  set   << "foo#{i}"
end

Benchmark.bm do |x|
  x.report("array") { 10_000.times { array.include?("foo9999") } }
  x.report("set  ") { 10_000.times { set.include?("foo9999")   } }
end

And the results:

      user     system      total        real
array  7.020000   0.000000   7.020000 (  7.031525)
set    0.010000   0.000000   0.010000 (  0.004816)
share|improve this answer
    
If you use detect, then you can at least reduce the looping. detect will stop at the first item 'detected' (the block passed for the item evaluates to true). In addition, you can tell detect what to do if nothing is detected (you can pass in a lambda). – aenw Jul 23 '14 at 21:04
1  
@aenw doesn't include? stop at first hit? – Kimmo Lehto Aug 4 '14 at 9:45
    
you're absolutely right. I'm so used to using detect that I'd forgotten that about include. thanks for your comment - it ensured that I refreshed my knowledge. – aenw Aug 6 '14 at 4:52

There are multiple ways to accomplish this. A few of them are as follows:

a = [1,2,3,4,5]

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false
share|improve this answer
2  
Note that Object#in? was only added to Rails (i.e. ActiveSupport) v3.1+. It is not available in core Ruby. – Tom Lord Jul 15 '16 at 10:10

This will tell you not only that it exists but also how many times it appears:

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1
share|improve this answer
10  
There's no sense in using this unless you want to know how many times it appears, though, as .any? will return as soon as it finds the first matching element, .count will always process the entire array. – Zaz Jul 26 '14 at 13:09

For what it's worth, The Ruby docs are an amazing resource for these kinds of questions.

I would also take note of the length of the array you're searching through. The include? method will run a linear search with O(n) complexity which can get pretty ugly depending on the size of the array.

If you're working with a large (sorted) array, I would consider writing a binary search algorithm which shouldn't be too difficult and has a worst case of O(log n).

Or if you're using Ruby 2.0, you can take advantage of bsearch.

share|improve this answer
3  
A binary search assumes the array is sorted (or ordered in some form) which can be costly for large arrays, often negating the advantage. – the Tin Man Jan 20 '16 at 22:41

There's the other way around, too!

Suppose the array is [ :edit, :update, :create, :show ] - well perhaps the entire seven deadly/restful sins :)

And further toy with the idea of pulling a valid action from some string - say

my brother would like me to update his profile

Solution

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}
share|improve this answer
3  
.......but why? – Rambatino Jul 17 '15 at 20:21

If you have on mind more values... you can try:

Example: if Cat and Dog exist in the array:

(['Cat','Dog','Bird'] & ['Cat','Dog'] ).size == 2   #or replace 2 with ['Cat','Dog].size

Instead of:

['Cat','Dog','Bird'].member?('Cat') and ['Cat','Dog','Bird'].include?('Dog')

Note: member? and include? are the same.

This can do the work in one line!

share|improve this answer

If we want to not use include? this also works:

['cat','dog','horse'].select{ |x| x == 'dog' }.any?
share|improve this answer
3  
any? also accepts blocks: ['cat','dog','horse'].any? { |x| x == 'dog' } – maikonas Nov 6 '14 at 8:54

How about this way?

['Cat', 'Dog', 'Bird'].index('Dog')
share|improve this answer
    
It's still going to iterate over the array just to find the element. Then it has to return the index of that element. – the Tin Man Jan 20 '16 at 22:35
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true
share|improve this answer

if you don't want to use include? you can first wrap the element in an array and then check whether the wrapped element is equal to the intersection of the array and the wrapped element. This will return a boolean value based on equality.

def in_array?(array, item)
    item = [item] unless item.is_a?(Array)
    item == array & item
end
share|improve this answer

Here is one more way to do this:

arr = ['Cat', 'Dog', 'Bird']
e = 'Dog'

present = arr.size != (arr - [e]).size
share|improve this answer
    
This is a horrendously inefficient way to do this! I'm not downvoting because it's not technically incorrect, and somebody might learn something about Ruby from reading it, but there are many better answers above. – jibberia Nov 28 '16 at 19:17
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
share|improve this answer

Ruby has 11 methods to find elements in an array.

The preferred one is include?

Or for repeated access, creating a set and then calling include? or member?

Here are all of them,

array.include?(element) # preferred method
array.member?(element)
array.to_set.include?(element)
array.to_set.member?(element)
array.index(element) > 0
array.find_index(element) > 0
array.index { |each| each == element } > 0
array.find_index { |each| each == element } > 0
array.any? { |each| each == element }
array.find { |each| each == element } != nil
array.detect { |each| each == element } != nil

All of them return a trueish value if the element is present.

include? is the preferred method. It uses a C-language for loop internally that breaks when an element matches the internal rb_equal_opt/rb_equal functions. It cannot get much more efficient unless you create a set for repeated membership checks.

VALUE
rb_ary_includes(VALUE ary, VALUE item)
{
  long i;
  VALUE e;

  for (i=0; i<RARRAY_LEN(ary); i++) {
    e = RARRAY_AREF(ary, i);
    switch (rb_equal_opt(e, item)) {
      case Qundef:
        if (rb_equal(e, item)) return Qtrue;
        break;
      case Qtrue:
        return Qtrue;
    }
  }
  return Qfalse;
}

member? is not redefined in Array class and uses an unoptimized implementation from the Enumerable module that literally enumerate through all elements.

static VALUE
member_i(RB_BLOCK_CALL_FUNC_ARGLIST(iter, args))
{
  struct MEMO *memo = MEMO_CAST(args);

  if (rb_equal(rb_enum_values_pack(argc, argv), memo->v1)) {
    MEMO_V2_SET(memo, Qtrue);
    rb_iter_break();
  }
  return Qnil;
}

static VALUE
enum_member(VALUE obj, VALUE val)
{
  struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

  rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
  return memo->v2;
}

Translated to Ruby code this does about the following

def member?(value)
  memo = [value, false, 0]
  each_with_object(memo) do |each, memo|
    if each == memo[0]
      memo[1] = true 
      break
    end
  memo[1]
end

Both include? and member? have O(n) time complexity since the both search the array for the first occurrence of the expected value.

We can use a set to get O(1) access time at the cost of having to create a hash representation of the array first. If you repeatedly check membership on the same array this initial investment can pay off quickly. Set is not implemented in C but as plain Ruby class, still the O(1) access time of the underlying @hash makes this worthwhile.

Here is the implementation of the Set class,

module Enumerable
  def to_set(klass = Set, *args, &block)
    klass.new(self, *args, &block)
  end
end

class Set
  def initialize(enum = nil, &block) # :yields: o
    @hash ||= Hash.new
    enum.nil? and return
    if block
      do_with_enum(enum) { |o| add(block[o]) }
    else
      merge(enum)
    end
  end

  def merge(enum)
    if enum.instance_of?(self.class)
      @hash.update(enum.instance_variable_get(:@hash))
    else
      do_with_enum(enum) { |o| add(o) }
    end
    self
  end

  def add(o)
    @hash[o] = true
    self
  end

  def include?(o)
    @hash.include?(o)
  end
  alias member? include?

  ...
end

As you can see the Set class just creates an internal @hash instance, maps all objects to true and then checks membership using Hash#include? which is implemented with O(1) access time in the Hash class.

I won't discuss the other 7 methods as they are all less efficient.

There are actually even more methods with O(n) complexity beyond the 11 listed above, but I decided to not list them since scan the entire array rather than breaking at the first match.

Don't use these,

# bad examples
array.grep(element).any? 
array.select { |each| each == element }.size > 0
...
share|improve this answer

Fun fact,

You can use * to check array membership in a case expressions.

case element
when *array 
  ...
else
  ...
end

Notice the little * in the when clause, this checks for membership in the array.

All the usual magic behavior of the splat operator applies, so for example if array is not actually an array but a single element it will match that element.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.