1537

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?

4
  • 6
    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
    Commented Feb 5, 2016 at 3:20
  • dont use include? method if you want to check multiples times for different value to be present in array or not because include? each time will iterate over array taking O(n) operation to search each time, Instead make a hash hash = arr.map {|x| [x,true]}.to_h, now check whether hash.has_key? 'Dog' returns true or not
    – aqfaridi
    Commented Apr 28, 2017 at 19:42
  • 5
    You can't really do it totally "without looping through it". It's logically impossible, the computer just can't know for sure whether the array contains the element without looping through the elements to check if any of them are the one it is searching for. Unless it's empty, of course. Then I guess you don't need a loop.
    – Tim M.
    Commented Jul 21, 2017 at 15:06
  • 1
    See the benchmarks below for tests of the difference of the various ways to find an element in an Array and a Set. stackoverflow.com/a/60404934/128421 Commented Feb 26, 2020 at 8:46

28 Answers 28

2239

You're looking for include?:

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
11
  • 80
    Alternate syntax: %w(Cat Dog Bird).include? 'Dog'
    – scarver2
    Commented Dec 18, 2012 at 22:04
  • 215
    Sometimes I wish it was "contains" not include. I always get it mixed up with includes. Commented Oct 9, 2013 at 2:11
  • 22
    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. Commented Dec 16, 2013 at 3:49
  • 9
    @HenleyChiu I which it was called [ 'Dog', 'Bird', 'Cat' ].has? 'Dog'
    – user1115652
    Commented May 21, 2014 at 23:01
  • 7
    @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" Commented Sep 10, 2016 at 15:45
302

There is an in? method in ActiveSupport (part of Rails) since v3.1, as pointed out by @campeterson. 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?...

2
  • 2
    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 Commented Aug 21, 2013 at 17:34
  • Thank you, specifically, for including commentary on the fact that the questioners original point of figuring out if something is in an array "without looping through it" is impossible, even if the loop may be hidden away behind an easy method call like Array#include? -- and how to actually avoid such (using Set), should that be important. This answer gets my up-vote, even though I might have left the bit about in? as an aside at the end.
    – lindes
    Commented Apr 17, 2021 at 5:30
178

Try

['Cat', 'Dog', 'Bird'].include?('Dog')
0
64

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  

See Enumerable for more information.

My inspiration came from "evaluate if array has any items in ruby"

1
  • 1
    Very useful if you want check any/all of those string is included in another string/constant
    – thanikkal
    Commented Jul 12, 2012 at 12:40
54

Ruby has eleven methods to find elements in an array.

The preferred one is include? or, for repeated access, creat a Set and then call 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

They all 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 the Array class and uses an unoptimized implementation from the Enumerable module that literally enumerates 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 seven 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 they 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
...
5
  • 1
    How brazen to say that Ruby has exactly 11 ways to do anything! No sooner than you say that someone will point out that you missed #12, then #13, and so on. To make my point I will suggest other ways, but first let me question the 11 ways you have enumerated. First, you can hardly count index and find_index (or find and detect) as separate methods, as they are just different names for the same method. Secondly, all the expressions that end with > 0 are incorrect, which I'm sure was an oversight. (cont.) Commented Jul 28, 2018 at 21:14
  • 1
    ...arr.index(e), for example, returns 0 if arr[0] == e. You will recall arr.index(e) returns nil if e is not present. index cannot be used, however, if one is searching for nil in arr. (Same problem with rindex, which is not listed.). Converting the array to a set and then employ set methods is a bit of stretch. Why not then convert to a hash (with keys from the array and arbitrary values), then use hash methods? Even if converting to a set is OK, there are other set methods that could be used, such as !arr.to_set.add?(e). (cont.) Commented Jul 28, 2018 at 21:22
  • ...As promised, here are are a few more methods that could be used: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e] and arr & [e] == [e]. One could also employ select and reject. Commented Jul 28, 2018 at 21:30
  • I'd highly recommend using a Set if the criteria is that no duplicates are allowed, and knowing whether a particular element exists in the list. Set is SOooo much faster. Arrays really shouldn't be used when searching is needed; They're better used as a queue where things are temporarily stored to be processed in order. Hash and Set are better for looking to see if something exists. Commented Jan 28, 2020 at 18:31
  • some_array.exclude?('some_string') is also useful.
    – not2qubit
    Commented Jan 27, 2022 at 15:33
52

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.

28

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
8
  • 11
    a.each do ... end Umm... not sure how that's not a loop
    – tckmn
    Commented Dec 15, 2013 at 0:57
  • 1
    That loop is performed only once, when the trie is constructed. It is a constant term that does not influence the algorithmic complexity. Bluntly, the algorithm has to loop through the array at least once to know what words are there at all. Once the trie is constructed, it can be used many times to check for the presence of a word with roughly logarithmic complexity. Commented Dec 16, 2013 at 3:44
  • 32
    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. Commented Dec 16, 2013 at 7:21
  • 4
    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
    Commented Feb 7, 2014 at 20:40
  • 11
    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. Commented Feb 13, 2014 at 22:48
22

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 points 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)
5
  • 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
    Commented Jul 23, 2014 at 21:04
  • 1
    @aenw doesn't include? stop at first hit? Commented Aug 4, 2014 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
    Commented Aug 6, 2014 at 4:52
  • 1
    include? stops at the first hit but if that hit is at the end of the list.... Any solution that relies on an Array for storage will have degrading performance as the list grows, especially when having to find an element at the end of the list. Hash and Set don't have that problem, nor would an ordered list and a binary-search. Commented Jan 28, 2020 at 18:37
  • That is pretty much what this answer was about in the first place :) Commented Jan 31, 2020 at 11:52
21

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.

For 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/}

This returns the index of the first word in the array that contains the letter 'o'.

1
  • index still iterates over the array, it just returns the value of the element. Commented Jan 20, 2016 at 22:37
15

Check exists

Use include?

Example:

arr = [1, 2, 3]
arr.include?(1) -> true
arr.include?(4) -> false

Check does not exist

Use exclude?

Example:

arr = %w(vietnam china japan)
arr.exclude?('usa') -> true
arr.exclude?('china') -> false
1
  • *.include?("some-string") also works for exact string matches of an array item.
    – not2qubit
    Commented Jan 27, 2022 at 15:30
14

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.

3
  • Good to know..! Commented Aug 16, 2018 at 19:41
  • It'd also be a slow first check in a case statement, so I'd use it in the last when possible so other, faster checks, get weeded out quickly. Commented Feb 6, 2020 at 5:30
  • Is there anyway to do case array and then use when element to indicate when the element is contained in the array? Commented Apr 17 at 11:29
11

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
1
  • 9
    Note that Object#in? was only added to Rails (i.e. ActiveSupport) v3.1+. It is not available in core Ruby.
    – Tom Lord
    Commented Jul 15, 2016 at 10:10
8

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!

7

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

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1
2
  • 13
    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
    Commented Jul 26, 2014 at 13:09
  • While this technically will tell whether something exists, it's also NOT the right way to do it if you care about speed. Commented Jan 28, 2020 at 18:41
6

If you need to check multiples times for any key, convert arr to hash, and now check in O(1)

arr = ['Cat', 'Dog', 'Bird']
hash = arr.map {|x| [x,true]}.to_h
 => {"Cat"=>true, "Dog"=>true, "Bird"=>true}
hash["Dog"]
 => true
hash["Insect"]
 => false

Performance of Hash#has_key? versus Array#include?

Parameter              Hash#has_key?                 Array#include 

Time Complexity         O(1) operation                O(n) operation 

Access Type             Accesses Hash[key] if it      Iterates through each element
                        returns any value then        of the array till it
                        true is returned to the       finds the value in Array
                        Hash#has_key? call
                        call    

For single time check using include? is fine

5
  • Are you not still looping through the array to convert it to a hash?
    – chrisjacob
    Commented Apr 28, 2017 at 19:43
  • 2
    yes, i did but now i have pre-computed answer to check whether any element exist in array or not. Now, next time when i search for other word, it will take O(1) for query, right although pre-computation will take O(n). Actually i have used include? in my application inside loop for checking whether particular element exist in the array or not, it ruins the performance, it checked in ruby-prof, that was the bottleneck
    – aqfaridi
    Commented Apr 28, 2017 at 19:47
  • 1
    ....but O(n) space and also, no it's O(n) time, because as @chris72205 rightly points out, you have to iterate through your array first. O(1) + O(n) = O(n). So in fact, this is way worse than include?
    – Rambatino
    Commented Jun 2, 2017 at 20:25
  • Dude i was just saying dont use include inside loop, for single time check using include is fine. So please read the use case first, its better if you need to check multiple times inside loop.
    – aqfaridi
    Commented Jun 2, 2017 at 20:44
  • Converting an array to a hash is costly, but using an array for searching is the wrong structure. Arrays are better as queues where order might be important; Hashes and Sets are better when inclusion/existence/uniqueness are important. Start with the right container and the problem is moot. Commented Jan 28, 2020 at 18:44
5

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.

3
  • 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. Commented Jan 20, 2016 at 22:41
  • A binary search also requires all pairs of elements to be comparable with <=>, which is not always the case. Suppose, for example, the elements of the array were hashes. Commented Jul 28, 2018 at 21:36
  • 1
    @CarySwoveland it should be more or less implied that the elements within a sorted array are comparable.
    – davissp14
    Commented Aug 16, 2018 at 19:12
4

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

['cat','dog','horse'].select{ |x| x == 'dog' }.any?
1
  • 3
    any? also accepts blocks: ['cat','dog','horse'].any? { |x| x == 'dog' }
    – maikonas
    Commented Nov 6, 2014 at 8:54
4

How about this way?

['Cat', 'Dog', 'Bird'].index('Dog')
1
  • It's still going to iterate over the array just to find the element. Then it has to return the index of that element. Commented Jan 20, 2016 at 22:35
4
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true
1
  • 1
    ['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil. Was nil found? Commented Jul 28, 2018 at 20:39
3

If you're trying to do this in a MiniTest unit test, you can use assert_includes. Example:

pets = ['Cat', 'Dog', 'Bird']
assert_includes(pets, 'Dog')      # -> passes
assert_includes(pets, 'Zebra')    # -> fails 
3

There's the other way around this.

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:

"my brother would like me to update his profile"

Then:

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}
4
  • 1
    Your regex, /[,|.| |]#{v.to_s}[,|.| |]/, makes me think you wanted to find 'the name of the action surrounded by one of: comma, period, space, or nothing at all', but there are some subtle bugs. "|update|" would return [:update] and "update" would return []. Character classes ([...]) don't use pipes (|) to separate characters. Even if we change them to groups ((...)), you can't match an empty character. So the regex you probably wanted is /(,|\.| |^)#{v.to_s}(,|\.| |$)/
    – bkDJ
    Commented May 29, 2017 at 13:21
  • the regex is ok (checked rubular.com) - and @Rambatino: the why would be like Amazon Echo for instance ;) You could say: "please add chicken to my shopping list" (and - well - then you'd have to add the :add to the array, but I reckon you get the gist of it ;)
    – walt_die
    Commented Jun 2, 2017 at 15:46
  • 1
    "the regex is ok (checked rubular.com)". It is not ok. Your regex will not match a keyword at the beginning or end of a string (e.g. "update my brother's profile"). If you don't want to match the beginning or end, then your regex is still not ok because the character class to either side of the keyword should be /[,. ]/
    – bkDJ
    Commented Jul 26, 2017 at 16:03
  • As @bkDJ says, the regex is wrong. rubular.com/r/4EG04rANz6KET6 Commented Jan 28, 2020 at 18:54
3

I always find it interesting to run some benchmarks to see the relative speed of the various ways of doing something.

Finding an array element at the start, middle or end will affect any linear searches but barely affect a search against a Set.

Converting an Array to a Set is going to cause a hit in processing time, so create the Set from an Array once, or start with a Set from the very beginning.

Here's the benchmark code:

# frozen_string_literal: true

require 'fruity'
require 'set'

ARRAY = (1..20_000).to_a
SET = ARRAY.to_set

DIVIDER = '-' * 20

def array_include?(elem)
  ARRAY.include?(elem)
end

def array_member?(elem)
  ARRAY.member?(elem)
end

def array_index(elem)
  ARRAY.index(elem) >= 0
end

def array_find_index(elem)
  ARRAY.find_index(elem) >= 0
end

def array_index_each(elem)
  ARRAY.index { |each| each == elem } >= 0
end

def array_find_index_each(elem)
  ARRAY.find_index { |each| each == elem } >= 0
end

def array_any_each(elem)
  ARRAY.any? { |each| each == elem }
end

def array_find_each(elem)
  ARRAY.find { |each| each == elem } != nil
end

def array_detect_each(elem)
  ARRAY.detect { |each| each == elem } != nil
end

def set_include?(elem)
  SET.include?(elem)
end

def set_member?(elem)
  SET.member?(elem)
end

puts format('Ruby v.%s', RUBY_VERSION)

{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include?        { array_include?(element)        }
    _array_member?         { array_member?(element)         }
    _array_index           { array_index(element)           }
    _array_find_index      { array_find_index(element)      }
    _array_index_each      { array_index_each(element)      }
    _array_find_index_each { array_find_index_each(element) }
    _array_any_each        { array_any_each(element)        }
    _array_find_each       { array_find_each(element)       }
    _array_detect_each     { array_detect_each(element)     }
  end
end

puts '', DIVIDER, 'Sets vs. Array.include?', DIVIDER
{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include? { array_include?(element) }
    _set_include?   { set_include?(element)   }
    _set_member?    { set_member?(element)    }
  end
end

Which, when run on my Mac OS laptop, results in:

Ruby v.2.7.0
--------------------
First
--------------------
Running each test 65536 times. Test will take about 5 seconds.
_array_include? is similar to _array_index
_array_index is similar to _array_find_index
_array_find_index is faster than _array_any_each by 2x ± 1.0
_array_any_each is similar to _array_index_each
_array_index_each is similar to _array_find_index_each
_array_find_index_each is faster than _array_member? by 4x ± 1.0
_array_member? is faster than _array_detect_each by 2x ± 1.0
_array_detect_each is similar to _array_find_each
--------------------
Middle
--------------------
Running each test 32 times. Test will take about 2 seconds.
_array_include? is similar to _array_find_index
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 2x ± 0.1
_array_member? is faster than _array_index_each by 2x ± 0.1
_array_index_each is similar to _array_find_index_each
_array_find_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each
--------------------
Last
--------------------
Running each test 16 times. Test will take about 2 seconds.
_array_include? is faster than _array_find_index by 10.000000000000009% ± 10.0%
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 3x ± 0.1
_array_member? is faster than _array_find_index_each by 2x ± 0.1
_array_find_index_each is similar to _array_index_each
_array_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each

--------------------
Sets vs. Array.include?
--------------------
--------------------
First
--------------------
Running each test 65536 times. Test will take about 1 second.
_array_include? is similar to _set_include?
_set_include? is similar to _set_member?
--------------------
Middle
--------------------
Running each test 65536 times. Test will take about 2 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 1400x ± 1000.0
--------------------
Last
--------------------
Running each test 65536 times. Test will take about 4 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 3000x ± 1000.0

Basically the results tell me to use a Set for everything if I'm going to search for inclusion unless I can guarantee that the first element is the one I want, which isn't very likely. There's some overhead when inserting elements into a hash, but the search times are so much faster I don't think that should ever be a consideration. Again, if you need to search it, don't use an Array, use a Set. (Or a Hash.)

The smaller the Array, the faster the Array methods will run, but they're still not going to keep up, though in small arrays the difference might be tiny.

"First", "Middle" and "Last" reflect the use of first, size / 2 and last for ARRAY for the element being searched for. That element will be used when searching the ARRAY and SET variables.

Minor changes were made for the methods that were comparing to > 0 because the test should be >= 0 for index type tests.

More information about Fruity and its methodology is available in its README.

2

it has many ways to find a element in any array but the simplest way is 'in ?' method.

example:
arr = [1,2,3,4]
number = 1
puts "yes #{number} is present in arr" if number.in? arr
2
  • 2
    Note: As described in this answer, the method in? requires ActiveSupport to be imported: require active_support.
    – Patrick
    Commented Jun 26, 2019 at 6:22
  • It doesn't require all of ActiveSupport if you use the core extensions. Commented Feb 6, 2020 at 5:37
2

If you want to return the value not just true or false, use

array.find{|x| x == 'Dog'}

This will return 'Dog' if it exists in the list, otherwise nil.

1
  • Or use array.any?{|x| x == 'Dog'} if you do want true/false (not the value), but also want to compare against a block like this.
    – mahemoff
    Commented Jun 30, 2019 at 13:02
2

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
1

Here is one more way to do this:

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

present = arr.size != (arr - [e]).size
4
  • 1
    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
    Commented Nov 28, 2016 at 19:17
  • Conehead, you can simplify to arr != arr - [e]. arr & [e] == [e] is another way along the same lines. Commented Jul 28, 2018 at 21:43
  • @CarySwoveland Do not make fun of a wizard's hat ;-)
    – Wand Maker
    Commented Jul 29, 2018 at 12:55
  • I was referring to the wizard’s head, not hat, with the greatest of respect. Commented Jul 29, 2018 at 14:56
0
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
0
0

Try below

(['Cat', 'Dog', 'Bird'] & ['Dog']).any?

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