Reputation: 588
I'm trying to shorten this code:
if a.nil?
a = "foo"
else
a << "foo"
end
a
is either nil
or a string.
I have already tried a ||= "foo"
and a += "foo"
but these don't work if a
is nil
.
Upvotes: 3
Views: 3649
Reputation: 998
If you want to combine 2 strings, where any one of them may be nil
, you could do:
a.nil? ? a = (b || '') : a << (b || '')
This works even if a
, b
or both are nil
.
This is also by far faster than alternative methods, even though the syntax is somewhat ugly.
Benchmark:
require 'benchmark'
n = 50_000_000
Benchmark.bm do |x|
x.report("compact.reduce(:+)") { n.times { a = nil; b = 'foo'; a = [a, b].compact.reduce(:+) } }
x.report("string interpolation") { n.times { a = nil; b = 'foo'; a = "#{a}#{b}" } }
x.report("to_s") { n.times { a = nil; b = 'foo'; a = a.to_s + b.to_s } }
x.report("(x || '') +") { n.times { a = nil; b = 'foo'; a = (a || '') + b } }
x.report("(x || '') <<") { n.times { a = nil; b = 'foo'; a = (a || '') << b } } # will throw an exception if used with `frozen_string_literal: true`
x.report("ternary") { n.times { a = nil; b = 'foo'; a.nil? ? a = (b || '') : a << (b || '') } }
end
Results:
user system total real
compact.reduce(:+) 6.677367 0.000000 6.677367 ( 6.677337)
string interpolation 7.983451 0.000000 7.983451 ( 7.983440)
to_s 7.067699 0.000000 7.067699 ( 7.067702)
(x || '') + 5.130624 0.000000 5.130624 ( 5.130585)
(x || '') << 4.809965 0.000000 4.809965 ( 4.809978)
ternary 3.300871 0.000000 3.300871 ( 3.300838)
Upvotes: 0
Reputation: 114248
It might be irrelevant in your case, but there's a difference between a +=
and a <<
.
+=
assigns a new string to a
, leaving the old one unchanged:
a = 'abc'
b = a
a += 'foo'
a #=> "abcfoo" # <- a refers to the new string object
b #=> "foo" # <- b still refers to the original string
This is because String#+
returns a new string. a += 'foo'
is equivalent to a = a + 'foo'
.
String#<<
on the other hand modifies the existing string, it doesn't create a new one:
a = 'abc'
b = a
a << 'foo'
a #=> "abcfoo"
b #=> "abcfoo" # <- both changed, because a and b refer to the same object
So, in order to shorten your code without changing its behavior, you could use:
(a ||= '') << 'foo'
a ||= ''
assigns an empty string to a
if a
is nil
. Then << 'foo'
appends 'foo'
to a
.
Upvotes: 5
Reputation: 30071
nil.to_s
is equals to ''
so you could write
a = a.to_s + 'foo'
or, an alternative
a = "#{a}foo"
Upvotes: 10
Reputation: 2610
write like below:
a = a.nil? ? (a.to_s + 'foo') : 'foo'
Upvotes: 0