Checking whether it is some falsy value or something - ruby

Is there a short way to write the following (so that x appears only once)?
x == nil or x == something
An important thing is that calling something is not done when x == nil is satisfied. The possibility of x being false does not need to be considered.

Instead x == nil you can test x.nil?.
So you could use:
x.nil? or x == something
or
x.nil? || x == something
The solution with include? needs an additional array, depending on your report it may need some additional workload (but I think that is unnecessary microtuning)
Nevertheless, here's a benchmark:
require 'benchmark'
TEST_LOOPS = 10_000_000
X = nil
Y = :something
Z = :something_else
Benchmark.bm(20) {|b|
b.report('nil? or') {
TEST_LOOPS.times {
X.nil? or X == :something
Y.nil? or Y == :something
Z.nil? or Z == :something
} #Testloops
} #b.report
b.report('nil? ||') {
TEST_LOOPS.times {
X.nil? || X == :something
Y.nil? || Y == :something
Z.nil? || Z == :something
} #Testloops
}
b.report('== nil or') {
TEST_LOOPS.times {
X== nil or X == :something
Y== nil or Y == :something
Z== nil or Z == :something
} #Testloops
} #b.report
b.report('== nil ||') {
TEST_LOOPS.times{
X== nil || X == :something
Y== nil || Y == :something
Z== nil || Z == :something
} #Testloops
} #b.report
#Only if X being false does not need to be considered.
b.report('!X ||') {
TEST_LOOPS.times{
!X || X == :something
!Y || Y == :something
!Z || Z == :something
} #Testloops
} #b.report
b.report('include?') {
TEST_LOOPS.times {
[nil, :something].include?(X)
[nil, :something].include?(Y)
[nil, :something].include?(Z)
} #Testloops
}
b.report('include? precompile') {
testarray = [nil, :something]
TEST_LOOPS.times {
testarray.include?(X)
testarray.include?(Y)
testarray.include?(Z)
} #Testloops
}
} #Benchmark
My result:
user system total real
nil? or 2.574000 0.000000 2.574000 ( 2.574000)
nil? || 2.543000 0.000000 2.543000 ( 2.542800)
== nil or 2.356000 0.000000 2.356000 ( 2.355600)
== nil || 2.371000 0.000000 2.371000 ( 2.371200)
!X || 1.856000 0.000000 1.856000 ( 1.856400)
include? 4.477000 0.000000 4.477000 ( 4.477200)
include? precompile 2.746000 0.000000 2.746000 ( 2.745600)
Stefans solution in a comment seems to be the fastest.

Related

Does overriding == for a object change the behavior of include? method of an array?

Example:
class CustomObject
.....
def ==(other)
self.x == other.x && self.y =! other.y
end
.....
end
array_of_custom_objects = CustomObject.load_for(company_id: company_id)
new_custom_object = CustomObject.new(....)
array_of_custom_objects.include? new_custom_object
My question is does the array include? method compare two objects bases on the defination of == method?
Bascially, will the above code determine whether my new_custom_object is included in the array of CustomObject by evaluating the overridden == method for each insance of CustomObject in the array with new_custom_object?
My question is does the array include? method compare two objects bases on the defination of == method?
Yes. As said in: https://ruby-doc.org/3.2.0/Array.html#method-i-include-3F
include?(obj) → true or false click to toggle source
Returns true if for some index i in self, obj == self[i]; otherwise false:
Seems to be working, (though I'm not sure if this is the most optimal way of doing things as we don't know the context of your code):
class CustomObject
attr_reader :x, :y, :z
def initialize(x, y, z)
#x = x
#y = y
#z = z
end
def ==(other)
self.x == other.x && self.y != other.y
end
end
custom_objects = []
new_custom_object_1 = CustomObject.new(1, 2, 3)
custom_objects << new_custom_object_1
new_custom_object_2 = CustomObject.new(2, 3, 4)
custom_objects << new_custom_object_2
search_object = CustomObject.new(2, 7, 4) # 2 == 2 && 3 != 7
puts custom_objects.include?(search_object)
# => true
search_object = CustomObject.new(2, 3, 4) # 2 == 2 && 3 != 3
puts custom_objects.include?(search_object)
# => false

Is there a better way to compare strings in a reasonable amount of time?

I have this Ruby function that tells me if two strings are "almost" equal, that is, if all characters in the string are identical and ordered in the same way except for one. So for instance, these are equal
equal
eual
but these are not
eal
equal
(two characters are missing in the above). So with help, I have come up with this
(lcs(a,b) == shortest && longest.length - shortest.length == 1)
in which las is defined by
def lcs(xstr, ystr)
return "" if xstr.empty? || ystr.empty?
x, xs, y, ys = xstr[0..0], xstr[1..-1], ystr[0..0], ystr[1..-1]
if x == y
x + lcs(xs, ys)
else
[lcs(xstr, ys), lcs(xs, ystr)].max_by {|x| x.size}
end
end
but my function is taking an extraordinarily long time. Note my benchmark below
2.4.0 :011 > timing = Benchmark.measure { StringHelper.lcs("navesxkolsky|1227000", "navsxkolsky|1227000") }
=> #<Benchmark::Tms:0x007fa1753830d8 #label="", #real=21.341279999993276, #cstime=0.0, #cutime=0.0, #stime=0.030000000000000027, #utime=21.28, #total=21.310000000000002>
Is there something I'm missing here that can get my comparison time down to like one second instead of 21?
Try this. The main idea is that if the method is to return false, it will do so as soon as that is known, even if rudundant code is required. (The method below still works if the line return false if (sz1-sz2).abs > 1 is removed.)
def equal_but_one?(str1, str2)
sz1 = str1.size
sz2 = str2.size
return false if (sz1-sz2).abs > 1
i = [sz1, sz2].max.times.find { |i| str1[i] != str2[i] }
return false if i.nil?
case sz1 <=> sz2
when 0
str1[i+1..-1] == str2[i+1..-1]
when -1
str2[i+1..-1] == str1[i..-1]
when 1
str1[i+1..-1] == str2[i..-1]
end
end
equal_but_one?('cat', 'cut') #=> true
equal_but_one?('bates', 'bats') #=> true
equal_but_one?('buss', 'bus') #=> true
equal_but_one?('cat', 'cat') #=> false
equal_but_one?('pig', 'pigs') #=> true
equal_but_one?('pig', 'pegs') #=> false
equal_but_one?('', '') #=> false
equal_but_one?('', 'a') #=> true
require 'benchmark'
Benchmark.measure { equal_but_one?("navesxkolsky|1227000", "navsxkolsky|1227000") }.real
#=> 1.6000005416572094e-05

How to check if array contains 3 consecutive duplicates?

I'm trying to return true if input (n) contains 3 duplicates in a row, else return false.
My code:
def got_three?(n)
n.each_cons(3) { |a, b, c| a == b && b == c ? true : false }
end
this is returning an error and I'm unsure as to why. Any help?
Try this one
n.each_cons(3).any? { |a, b, c| a == b && b == c }
No need to chain methods, just use Enumerable#each_cons with a block:
array.each_cons(3) { |f, s, t| break true if f == t && s == t}
Returns true if the array contains 3 duplicates in a row, otherwise nil (simple use !! if you need exactly true or false).
Benchmarks:
require 'benchmark'
array = (0..1000).to_a.push(*[3, 3, 3])
Benchmark.bmbm(20) do |x|
x.report(:chain) do
array.each_cons(3).any? { |a, b, c| a == b && b == c }
end
x.report(:one_block) do
array.each_cons(3) { |f, s, t| break true if f == t && s == t}
end
end
Result:
user system total real
chain 0.000000 0.000000 0.000000 ( 0.000167)
one_block 0.000000 0.000000 0.000000 ( 0.000101)
As you can see, this solution is more than 1.6x times faster. Happy coding :)

Array.map { |x| change value } is removing it from the array, why?

The objective is to move each letter to the next letter in the alphabet,
within the map, it successfully changed the letter but once i'm out of there the value disappears, except the vowels. How come?
def LetterChanges(str)
abc = [*("a".."z")]
result = str.split(//)
result.map! do |x|
if abc.include?(x)
if x == "z"
x = "A"
else
x = abc[abc.index(x)+1]
# if you puts x here, you can see it changes value correctly
if x == "a" || x == "e" || x == "i" || x == "o" || x == "u"
x.capitalize!
end
end
end
#However here, the changed values that are not vowels disappear
# WHY is that happening, is the second if (vowel) affecting it? How?
end
puts "#{result.join}" #<--- its only putting the vowels
return result.join
end
LetterChanges("what the hell is going on?")
The block passed to map! needs to return a value in all cases for this to work.
http://www.ruby-doc.org/core-2.2.0/Array.html#method-i-map-21
def LetterChanges(str)
abc = [*("a".."z")]
result = str.split(//)
result.map! do |x|
if abc.include?(x)
if x == "z"
x = "A"
else
x = abc[abc.index(x)+1]
if x == "a" || x == "e" || x == "i" || x == "o" || x == "u"
x.capitalize!
end
end
end
x
end
result.join
end
The problem is your if. When x is a not a vowel that return nil.
Just Change this line
if x == "a" || x == "e" || x == "i" || x == "o" || x == "u"
x.capitalize!
end
With this
x = %w{a e i o u}.include?(x) ? x.capitalize : x

If expression.nil? then expression else other expression

I find myself often seeing a pattern when I want to evaluate some expression, and if true give the result of the expression, or if false perform some other similar expression. For example
if hours.detect { |h| h.position > i && h.open == true }.nil?
hours.detect { |h| h.position >= 0 && h.open == true }
else
hours.detect { |h| h.position > i && h.open == true }
end
This code seems very redundant. Can someone suggest a more elegant way of doing this?
Thanks!
Try the code:
hh = hours.select {|h| h.open == true }
hh.detect { |h| h.position > i } || hh.detect { |h| h.position >= 0 }
In most cases (when h.open returns a boolean value) it can be transformed into the following one:
hh = hours.select {|h| h.open }
hh.detect { |h| h.position > i } || hh.detect { |h| h.position >= 0 }
You can first create a method
def detect_hours(i)
hours.detect { |h| h.position > i && h.open }
end
then just use below
detect_hours(i) || detect_hours(0)
I would do as below :
hh_true = hours.select {|h| h.open == true }
prc = prc hh_true.detect { |h| h.position >= 0 }
hh_true.detect(prc) { |h| h.position > i }
One example to illustrate this :
ary = ['fooo','fooo','foo']
prc = proc {ary.find{|e| e.size == 3}}
ary.detect(prc){|e| e.size == 5}
# => "foo"
ary.detect(prc){|e| e.size == 4 }
# => "fooo"
hours.sort! { |h1,h2|
((-1 if h1.open == false) || (1 if h1.position > i) || (0 if h1.position >= 0)) <=>
((-1 if h2.open == false) || (1 if h2.position > i) || (0 if h2.position >= 0))
}
hours[0] if hours[0].open
I think this works, but I'm not 100% sure. Basically, map hours to a number, sort those numbers, and it'll be the first element.
This is better if you don't expect to have any h.position > i cases, since you'll have to check the entire array anyway.
Alternatively, although I'm not sure on the syntax, use the ifnone feature in detect:
hours.find(lambda{hours.find{|h| h.open && h.position > 0}}){|h| h.open && h.position > i}
The most straightforward solution is:
hours.detect { |h| h.position > i && h.open == true } || \
hours.detect { |h| h.position >= 0 && h.open == true }
But is it the most efficient? At first glance, one would think not, that it is better to first select those hours for which h.open == true. But recall that detect quits once it finds a true condition, which could be the first element of hours in the first block or the last element of hours in the second block. Relative efficiency will depend on the data. If h.position > i is usually true, this may well be faster than first doing a select, which requires a complete traversal of hours. If efficiency is important, one might test this, as well as the effect of reversing the two &&'ed conditions in each block.
hours.detect { |h| h.position > i && h.open == true } is repeated exactly twice. In many languages you can store it into some variable:
var1 = hours.detect { |h| h.position > i && h.open == true }
then in your if, you can substitute it:
if var1.nil?
hours.detect { |h| h.position >= 0 && h.open == true }
else
var1
end
You can get rid off nil? by using unless:
var1 = hours.detect { |h| h.position > i && h.open == true }
unless var1
hours.detect { |h| h.position >= 0 && h.open == true }
else
var1
end
In this if, hours.detect is exact the same. You can extract it into method/lambda/proc. Here is method version:
def hourse_detect hours, &lam # `&` change block of code into variable(`proc` / `lambda`) OR variable(`proc` / `lambda`) into block of code
hours.detect &lam #detect need block of code not `proc` / `lambda`
end
and your you can store blocks of code that exist between { and } (or do / end):
block1 = proc { |h| h.position > i && h.open == true }
block2 = proc { |h| h.position >= 0 && h.open == true }
Here is full code using above feautures:
def hourse_detect hours, &lam
hours.detect &lam
end
block1 = proc { |h| h.position > i && h.open == true }
block2 = proc { |h| h.position >= 0 && h.open == true }
var1 = hourse_detect hours, &block1
unless var1
hours_detect hours, &block2
else
var1
end
h.open == truecan be transformed into:
def opened? hour
hours.open == true
end

Resources