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?
hash = arr.map {|x| [x,true]}.to_h
, now check whether hash.has_key? 'Dog'
returns true or not
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 Enumerable
s 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?
...
#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
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.
Try
['Cat', 'Dog', 'Bird'].include?('Dog')
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"
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.
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 true
ish 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
...
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.)
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.)
arr.count(e) > 0
, arr != arr.dup.delete(e)
, arr != arr - [e]
and arr & [e] == [e]
. One could also employ select
and reject
.
some_array.exclude?('some_string')
is also useful.
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
a.each do ... end
Umm... not sure how that's not a loop
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.
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)
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)
include?
stop at first hit?
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.
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'.
index
still iterates over the array, it just returns the value of the element.
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.
when
possible so other, faster checks, get weeded out quickly.
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
Object#in?
was only added to Rails (i.e. ActiveSupport
) v3.1+. It is not available in core Ruby.
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
*.include?("some-string")
also works for exact string matches of an array item.
This will tell you not only that it exists but also how many times it appears:
a = ['Cat', 'Dog', 'Bird']
a.count("Dog")
#=> 1
.any?
will return as soon as it finds the first matching element, .count
will always process the entire array.
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!
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
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
.
<=>
, which is not always the case. Suppose, for example, the elements of the array were hashes.
If we want to not use include?
this also works:
['cat','dog','horse'].select{ |x| x == 'dog' }.any?
How about this way?
['Cat', 'Dog', 'Bird'].index('Dog')
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true
['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil
. Was nil
found?
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
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}[,|.| |]/}
/[,|.| |]#{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}(,|\.| |$)/
/[,. ]/
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.
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.
array.any?{|x| x == 'Dog'}
if you do want true/false (not the value), but also want to compare against a block like this.
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
Here is one more way to do this:
arr = ['Cat', 'Dog', 'Bird']
e = 'Dog'
present = arr.size != (arr - [e]).size
arr != arr - [e]
. arr & [e] == [e]
is another way along the same lines.
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
in?
requires ActiveSupport
to be imported: require active_support
.
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
Success story sharing
%w(Cat Dog Bird).include? 'Dog'
#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.[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'