How do I pick randomly from an array? - ruby

I want to know if there is a much cleaner way of doing this. Basically, I want to pick a random element from an array of variable length. Normally, I would do it like this:
myArray = ["stuff", "widget", "ruby", "goodies", "java", "emerald", "etc" ]
item = myArray[rand(myarray.length)]
Is there something that is more readable / simpler to replace the second line? Or is that the best way to do it. I suppose you could do myArray.shuffle.first, but I only saw #shuffle a few minutes ago on SO, I haven't actually used it yet.

Just use Array#sample:
[:foo, :bar].sample # => :foo, or :bar :-)
It is available in Ruby 1.9.1+. To be also able to use it with an earlier version of Ruby, you could require "backports/1.9.1/array/sample".
Note that in Ruby 1.8.7 it exists under the unfortunate name choice; it was renamed in later version so you shouldn't use that.
Although not useful in this case, sample accepts a number argument in case you want a number of distinct samples.

myArray.sample(x) can also help you to get x random elements from the array.

myArray.sample
will return 1 random value.
myArray.shuffle.first
will also return 1 random value.

Random Number of Random Items from an Array
def random_items(array)
array.sample(1 + rand(array.count))
end
Examples of possible results:
my_array = ["one", "two", "three"]
my_array.sample(1 + rand(my_array.count))
=> ["two", "three"]
=> ["one", "three", "two"]
=> ["two"]

Here are some benchmark tests I performed on some of the answers posted here, using sample was consistently faster than the rest.
test_arr = ["stuff", "widget", "ruby", "goodies", "java", "emerald" ]
Benchmark.ips do |x|
x.report("1 - sample") { test_arr.sample }
x.report("2 - shuffle") { test_arr.shuffle.first }
x.report("3 - length") { rand(test_arr.length) }
x.report("4 - rand rand") { test_arr.sample(1 + rand(test_arr.count)) }
x.report("5 - rand el") { test_arr[rand(test_arr.count)]}
x.report("6 - switch") {
case rand(0..test_arr.length)
when 0
test_arr[0]
when 1
test_arr[1]
when 2
test_arr[2]
when 3
test_arr[3]
when 4
test_arr[4]
when 5
test_arr[5]
end
}
x.compare!
The tests were run on a MacBook Pro (15-inch, 2018), 2.6 GHz 6-Core Intel Core i7, 32 GB 2400 MHz DDR4
Warming up --------------------------------------
1 - sample 713.455k i/100ms
2 - shuffle 253.848k i/100ms
3 - length 489.078k i/100ms
4 - rand rand 236.396k i/100ms
5 - rand el 447.244k i/100ms
6 - switch 419.272k i/100ms
Calculating -------------------------------------
1 - sample 7.505M (± 3.2%) i/s - 37.813M in 5.044078s
2 - shuffle 2.661M (± 2.1%) i/s - 13.454M in 5.057659s
3 - length 5.021M (± 1.6%) i/s - 25.432M in 5.066159s
4 - rand rand 2.352M (± 2.4%) i/s - 11.820M in 5.029415s
5 - rand el 4.452M (± 2.2%) i/s - 22.362M in 5.025623s
6 - switch 4.324M (± 1.1%) i/s - 21.802M in 5.043294s
Comparison:
1 - sample: 7504636.7 i/s
3 - length: 5021326.6 i/s - 1.49x (± 0.00) slower
5 - rand el: 4452078.6 i/s - 1.69x (± 0.00) slower
6 - switch: 4323511.6 i/s - 1.74x (± 0.00) slower
2 - shuffle: 2661267.7 i/s - 2.82x (± 0.00) slower
4 - rand rand: 2351630.7 i/s - 3.19x (± 0.00) slower

arr = [1,9,5,2,4,9,5,8,7,9,0,8,2,7,5,8,0,2,9]
arr[rand(arr.count)]
This will return a random element from array.
If You will use the line mentioned below
arr[1+rand(arr.count)]
then in some cases it will return 0 or nil value.
The line mentioned below
rand(number)
always return the value from 0 to number-1.
If we use
1+rand(number)
then it may return number and arr[number] contains no element.

class String
def black
return "\e[30m#{self}\e[0m"
end
def red
return "\e[31m#{self}\e[0m"
end
def light_green
return "\e[32m#{self}\e[0m"
end
def purple
return "\e[35m#{self}\e[0m"
end
def blue_dark
return "\e[34m#{self}\e[0m"
end
def blue_light
return "\e[36m#{self}\e[0m"
end
def white
return "\e[37m#{self}\e[0m"
end
def randColor
array_color = [
"\e[30m#{self}\e[0m",
"\e[31m#{self}\e[0m",
"\e[32m#{self}\e[0m",
"\e[35m#{self}\e[0m",
"\e[34m#{self}\e[0m",
"\e[36m#{self}\e[0m",
"\e[37m#{self}\e[0m" ]
return array_color[rand(0..array_color.size)]
end
end
puts "black".black
puts "red".red
puts "light_green".light_green
puts "purple".purple
puts "dark blue".blue_dark
puts "light blue".blue_light
puts "white".white
puts "random color".randColor

Related

Removing multiple zeros from a string if only zeros are input

I'm trying to implement a method that if the user inputs a series of zero like "00000" it should return "0"
Otherwise, if the user inputs for example "00009" or "90000", the input variable should remain unchanged.
Is there a simple solution for this problem?
def doit(str)
str.squeeze == '0' ? '0' : str
end
doit("00000") #=> "0"
doit("00009") #=> "00009"
doit("90000") #=> "90000"
See the last sentence of the doc String#squeeze.
Try this
"0000000".gsub(/^0+$/, '0')
=> "0"
"0000009".gsub(/^0+$/, '0')
=> "0000009"
=> "0"
"9000000".gsub(/^0+$/, '0')
=> "9000000"
You can do it using regular expressions as follow:
def validate_sequence(sequence:)
r = /\A(^0+[1-9]+\z)|(\A[1-9]+[0-9]+\z)/
sequence.match?(r) ? sequence : '0'
end
The code above returns the same sequence if the sequence matches the regex or 0 in another case, the regex validates that a sequence is a valid number or if end with a valid sequence of numbers, the double ? change the result of the match function and returns a boolean value.
You can use the code above as follow: validate_sequence(sequence: '0000000')
If you want to only match digits distinct of 0 at the beginning and at the end you can do it by changing the value of r like this: r = /(^0+[1-9]+$)|(^[1-9]+0+$)/.
When the string always contains only digits then you might want to consider:
sequence.to_i.zero? ? '0' : sequence
IMHO it is always interesting to run a benchmark when there are so many different solutions
require 'benchmark/ips'
Benchmark.ips do |x|
examples = %w[00000 00001 12345]
def cary_swoveland(str)
str.squeeze == '0' ? '0' : str
end
def r4cc00n(str)
r = /(^0+[1-9]+$)|(^[1-9]+[0-9]+$)/
!!str.match(r) ? str : '0'
end
def spickermann(str)
str.to_i.zero? ? '0' : str
end
def ursus(str)
str.gsub(/^0+$/, '0')
end
%w[cary_swoveland r4cc00n spickermann ursus].each do |algorithm|
x.report(algorithm) { examples.each { |e| send(algorithm, e) } }
end
x.compare!
end
Result:
$ ruby benchmark.rb
Warming up --------------------------------------
cary_swoveland 86.149k i/100ms
r4cc00n 24.258k i/100ms
spickermann 94.211k i/100ms
ursus 21.714k i/100ms
Calculating -------------------------------------
cary_swoveland 833.459k (± 2.2%) i/s - 4.221M in 5.067333s
r4cc00n 231.787k (± 6.9%) i/s - 1.164M in 5.050636s
spickermann 905.658k (±10.0%) i/s - 4.522M in 5.097849s
ursus 213.674k (± 2.3%) i/s - 1.086M in 5.083811s
Comparison:
spickermann: 905658.0 i/s
cary_swoveland: 833459.1 i/s - same-ish: difference falls within error
r4cc00n: 231787.4 i/s - 3.91x (± 0.00) slower
ursus: 213674.2 i/s - 4.24x (± 0.00) slower

How do you use split and scan to parse a URI in ruby?

Let's say I have this string in Ruby
str = "/server/ab/file.html
I want to get an array that contains
["/server/", "/ab/", "file.html"]
Is there a way to obtain this array using split or scan? I have tried all kinds of combinations with nothing matching exactly what I want. I can't use any outside libraries. Any ideas? Thanks.
As #sawa stated, the issue is with the double '/' that requires you to manipulate the string.
The most direct solution I can think of is:
# removes the '/' at the beginning of the string
# and splits the string to an array
a = str.sub(/^\//, '').split('/') # => ["server", "ab", "file.html"]
# iterates through the array objects EXCEPT the last one,
# (notice three dots '...' instead of two '..'),
# and adds the missing '/'
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')} # => ["/server/", "/ab/"]
a # => ["/server/", "/ab/", "file.html"]
EDIT 2
Following up with #mudasobwa's concepts, ideas and inputs, if you know that the first character is always a '/', this would be fastest solution so far (see edited benchmark):
a = str[1..-1].split('/')
a << (a.pop.tap { a.map! {|s| "/#{s}/" } } )
Good Luck.
Benchmarks
After reading #mudasobwa's answer I was super impressed. I wanted to know how much faster his solution was...
... and I was surprised to see that although his solution is much more elegant looking, it's substantially slower.
I have no idea why, but it seems that the Regexp lookup using gsub or scan is slower in this case.
Here's the benchmark, for anyone interested (iterations per second - higher numbers are better):
require 'benchmark/ips'
str = "/server/ab/file.html"
Benchmark.ips do |b|
b.report("split") do
a = str.sub(/^\//, '').split('/')
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')}
end
b.report("updated split") do
a = str[1..-1].split('/')
a[0...-1].each {|s| s << '/'; s.insert(0 , '/')}
end
b.report("scan") do
str.scan(/(?<=\/)([\w.]+)(\/)?/).map { |(val,slash)| slash ? "/#{val}/" : val }
end
b.report("gsub") do
str.gsub(/(?<=\/)([\w.]+)(\/)?/).map { |m| "#{$2 && '/'}#{m}" }
end
b.report("mudasobwa's varient") do
a = str[1..-1].split('/')
[*a[0..-2].map { |e| "/#{e}/"}, a[-1]]
end
b.report("mudasobwa's tap concept") do
a = str[1..-1].split('/')
a << (a.pop.tap { a.map! {|s| "/#{s}/" } })
end
end; nil
# results:
#
# Calculating -------------------------------------
# split 39.378k i/100ms
# updated split 45.530k i/100ms
# scan 23.910k i/100ms
# gsub 18.006k i/100ms
# mudasobwa's varient 47.389k i/100ms
# mudasobwa's tap concept
# 51.895k i/100ms
# -------------------------------------------------
# split 517.487k (± 2.9%) i/s - 2.599M
# updated split 653.271k (± 6.4%) i/s - 3.278M
# scan 268.048k (± 6.9%) i/s - 1.339M
# gsub 202.457k (± 3.2%) i/s - 1.026M
# mudasobwa's varient 656.734k (± 4.8%) i/s - 3.317M
# mudasobwa's tap concept
# 761.914k (± 3.2%) i/s - 3.840M
▶ str.gsub(/(?<=\/)([\w.]+)(\/)?/).map { |m| "#{$2 && '/'}#{m}" }
#⇒ [ "/server/", "/ab/", "file.html" ]
or, with scan, that is more semantic:
▶ str.scan(/(?<=\/)([\w.]+)(\/)?/).map { |(val,slash)| slash ? "/#{val}/" : val }
Probably the fastest solution:
▶ a = str[1..-1].split('/')
▶ [*a[0..-2].map { |e| "/#{e}/"}, a[-1]]
#⇒ ["/server/", "/ab/", "file.html"]
Complete inplace array change (hey, aesthetes):
▶ a = str[1..-1].split('/')
▶ a.pop.tap do |e|
▷ a.map! do |e|
▷ [-1, 0].each do |i|
▷ e.insert(i, '/')
▷ end
▷ e
▷ end.push e
▷ end
▶ puts a
#⇒ ["/server/", "/ab/", "file.html"]
str = str[1..-1].split('/')
=> ["server", "ab", "file.html"]
str[0...-1].map!{|e| "/#{e}/"} << str[-1]
=> ["/server/", "/ab/", "file.html"]

Which way is efficient for storing in hash?

Assume my_hash = {:name => "bob", :age => 21}. I can assign values to the hash in three ways:
Way 1
my_hash[:name] = "bob"
my_hash[:age] = 21
Way 2
my_hash.store(:name,"bob")
my_hash.store(:age,21)
Way 3
my_hash = {:name => "bob", :age => 21}
Please help me understand value assignment in terms of OS memory. Why are there three ways to assign values to keys, and which way is efficient?
Incase of memory i believe all takes equal memory. I benchmarked each step and these are the results. As you can see speed for each case is just marginally different, not enough difference to choose one over the other.
So you just use the code that you feel natural when writing your code.
user system total real
0.760000 0.030000 0.790000 ( 0.808573) my_hash[:t] = 1
0.810000 0.030000 0.840000 ( 0.842075) my_hash.store(:t, 1)
0.750000 0.020000 0.770000 ( 0.789766) my_hash = {:t => 1}
benchmarking script.
require 'benchmark'
Benchmark.bm do |x|
x.report do
1000000.times do
my_hash = {}
my_hash[:t] = 1
my_hash[:b] = 2
end
end
x.report do
1000000.times do
my_hash = {}
my_hash.store(:t, 1)
my_hash.store(:b, 2)
end
end
x.report do
1000000.times do
my_hash = {:t => 1, :t => 2}
end
end
end
I prefer benchmark-ips for this sort of thing, because it works out how many times the test should be performed and it also gives you some error margins. For this
Benchmark.ips do |x|
x.report('[]') do |n|
n.times do
t = {}
t[:x] = 1
end
end
x.report('store') do |n|
n.times do
t = {}
t.store(:x, 1)
end
end
end
produces
[] 2.082M (±14.6%) i/s - 10.276M
store 1.978M (±13.9%) i/s - 9.790M
i.e. the difference is well within the margin of error, This isn't surprising because if you look at the source then you can see that []= and store are actually exactly the same method.

How do I know what iteration I'm in when using the Integer.times method?

Let's say I have
some_value = 23
I use the Integer's times method to loop.
Inside the iteration, is there an easy way, without keeping a counter, to see what iteration the loop is currently in?
Yes, just have your block accept an argument:
some_value.times{ |index| puts index }
#=> 0
#=> 1
#=> 2
#=> ...
or
some_value.times do |index|
puts index
end
#=> 0
#=> 1
#=> 2
#=> ...
3.times do |i|
puts i*100
end
In this way, you can replace 3 with any integer you like, and manipulate the index i in your looped calculations.
My example will print the following, since the index starts from 0:
# output
0
100
200

How to check if a value exists in an array in Ruby

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?
You're looking for include?:
>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
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?...
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 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
...
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
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)
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'.
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.
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
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
This will tell you not only that it exists but also how many times it appears:
a = ['Cat', 'Dog', 'Bird']
a.count("Dog")
#=> 1
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.
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
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}[,|.| |]/}
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.
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
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.
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
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
Try below
(['Cat', 'Dog', 'Bird'] & ['Dog']).any?

Resources