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?
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?
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']
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
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
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.
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