Reputation: 28760
I am looking for a more elegant way of concatenating strings in Ruby.
I have the following line:
source = "#{ROOT_DIR}/" << project << "/App.config"
Is there a nicer way of doing this?
And for that matter what is the difference between <<
and +
?
Upvotes: 405
Views: 625925
Reputation: 3031
For Puppet:
$username = 'lala'
notify { "Hello ${username.capitalize}":
withpath => false,
}
Upvotes: 0
Reputation: 574
You may use +
or <<
operator, but in ruby .concat
function is the most preferable one, as it is much faster than other operators. You can use it like.
source = "#{ROOT_DIR}/".concat(project.concat("/App.config"))
Upvotes: 2
Reputation: 11186
For your particular case you could also use Array#join
when constructing file path type of string:
string = [ROOT_DIR, project, 'App.config'].join('/')]
This has a pleasant side effect of automatically converting different types to string:
['foo', :bar, 1].join('/')
=>"foo/bar/1"
Upvotes: 0
Reputation: 2711
You can concatenate in string definition directly:
nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"
Upvotes: 2
Reputation: 6394
You can also use %
as follows:
source = "#{ROOT_DIR}/%s/App.config" % project
This approach works with '
(single) quotation mark as well.
Upvotes: 2
Reputation: 39
Situation matters, for example:
# this will not work
output = ''
Users.all.each do |user|
output + "#{user.email}\n"
end
# the output will be ''
puts output
# this will do the job
output = ''
Users.all.each do |user|
output << "#{user.email}\n"
end
# will get the desired output
puts output
In the first example, concatenating with +
operator will not update the output
object,however, in the second example, the <<
operator will update the output
object with each iteration. So, for the above type of situation, <<
is better.
Upvotes: 1
Reputation: 13467
Here's another benchmark inspired by this gist. It compares concatenation (+
), appending (<<
) and interpolation (#{}
) for dynamic and predefined strings.
require 'benchmark'
# we will need the CAPTION and FORMAT constants:
include Benchmark
count = 100_000
puts "Dynamic strings"
Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
bm.report("concat") { count.times { 11.to_s + '/' + 12.to_s } }
bm.report("append") { count.times { 11.to_s << '/' << 12.to_s } }
bm.report("interp") { count.times { "#{11}/#{12}" } }
end
puts "\nPredefined strings"
s11 = "11"
s12 = "12"
Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
bm.report("concat") { count.times { s11 + '/' + s12 } }
bm.report("append") { count.times { s11 << '/' << s12 } }
bm.report("interp") { count.times { "#{s11}/#{s12}" } }
end
output:
Dynamic strings
user system total real
concat 0.050000 0.000000 0.050000 ( 0.047770)
append 0.040000 0.000000 0.040000 ( 0.042724)
interp 0.050000 0.000000 0.050000 ( 0.051736)
Predefined strings
user system total real
concat 0.030000 0.000000 0.030000 ( 0.024888)
append 0.020000 0.000000 0.020000 ( 0.023373)
interp 3.160000 0.160000 3.320000 ( 3.311253)
Conclusion: interpolation in MRI is heavy.
Upvotes: 10
Reputation: 4114
from http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/
Using <<
aka concat
is far more efficient than +=
, as the latter creates a temporal object and overrides the first object with the new object.
require 'benchmark'
N = 1000
BASIC_LENGTH = 10
5.times do |factor|
length = BASIC_LENGTH * (10 ** factor)
puts "_" * 60 + "\nLENGTH: #{length}"
Benchmark.bm(10, '+= VS <<') do |x|
concat_report = x.report("+=") do
str1 = ""
str2 = "s" * length
N.times { str1 += str2 }
end
modify_report = x.report("<<") do
str1 = "s"
str2 = "s" * length
N.times { str1 << str2 }
end
[concat_report / modify_report]
end
end
output:
____________________________________________________________
LENGTH: 10
user system total real
+= 0.000000 0.000000 0.000000 ( 0.004671)
<< 0.000000 0.000000 0.000000 ( 0.000176)
+= VS << NaN NaN NaN ( 26.508796)
____________________________________________________________
LENGTH: 100
user system total real
+= 0.020000 0.000000 0.020000 ( 0.022995)
<< 0.000000 0.000000 0.000000 ( 0.000226)
+= VS << Inf NaN NaN (101.845829)
____________________________________________________________
LENGTH: 1000
user system total real
+= 0.270000 0.120000 0.390000 ( 0.390888)
<< 0.000000 0.000000 0.000000 ( 0.001730)
+= VS << Inf Inf NaN (225.920077)
____________________________________________________________
LENGTH: 10000
user system total real
+= 3.660000 1.570000 5.230000 ( 5.233861)
<< 0.000000 0.010000 0.010000 ( 0.015099)
+= VS << Inf 157.000000 NaN (346.629692)
____________________________________________________________
LENGTH: 100000
user system total real
+= 31.270000 16.990000 48.260000 ( 48.328511)
<< 0.050000 0.050000 0.100000 ( 0.105993)
+= VS << 625.400000 339.800000 NaN (455.961373)
Upvotes: 32
Reputation: 14743
You can do that in several ways:
<<
but that is not the usual wayWith string interpolation
source = "#{ROOT_DIR}/#{project}/App.config"
with +
source = "#{ROOT_DIR}/" + project + "/App.config"
The second method seems to be more efficient in term of memory/speed from what I've seen (not measured though). All three methods will throw an uninitialized constant error when ROOT_DIR is nil.
When dealing with pathnames, you may want to use File.join
to avoid messing up with pathname separator.
In the end, it is a matter of taste.
Upvotes: 625
Reputation: 67
Here are more ways to do this:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
And so on ...
Upvotes: 5
Reputation: 12578
Concatenation you say? How about #concat
method then?
a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object
In all fairness, concat
is aliased as <<
.
Upvotes: 5
Reputation: 156
Let me show to you all my experience with that.
I had an query that returned 32k of records, for each record I called a method to format that database record into a formated string and than concatenate that into a String that at the end of all this process wil turn into a file in disk.
My problem was that by the record goes, around 24k, the process of concatenating the String turned on a pain.
I was doing that using the regular '+' operator.
When I changed to the '<<' was like magic. Was really fast.
So, I remembered my old times - sort of 1998 - when I was using Java and concatenating String using '+' and changed from String to StringBuffer (and now we, Java developer have the StringBuilder).
I believe that the process of + / << in Ruby world is the same as + / StringBuilder.append in the Java world.
The first reallocate the entire object in memory and the other just point to a new address.
Upvotes: 6
Reputation: 27790
I'd prefer using Pathname:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
about <<
and +
from ruby docs:
+
: Returns a new String containing other_str concatenated to str
<<
: Concatenates the given object to str. If the object is a Fixnum between 0 and 255, it is converted to a character before concatenation.
so difference is in what becomes to first operand (<<
makes changes in place, +
returns new string so it is memory heavier) and what will be if first operand is Fixnum (<<
will add as if it was character with code equal to that number, +
will raise error)
Upvotes: 7
Reputation: 3326
The +
operator is the normal concatenation choice, and is probably the fastest way to concatenate strings.
The difference between +
and <<
is that <<
changes the object on its left hand side, and +
doesn't.
irb(main):001:0> s = 'a'
=> "a"
irb(main):002:0> s + 'b'
=> "ab"
irb(main):003:0> s
=> "a"
irb(main):004:0> s << 'b'
=> "ab"
irb(main):005:0> s
=> "ab"
Upvotes: 108
Reputation: 2199
If you are just concatenating paths you can use Ruby's own File.join method.
source = File.join(ROOT_DIR, project, 'App.config')
Upvotes: 81
Reputation: 8120
Since this is a path I'd probably use array and join:
source = [ROOT_DIR, project, 'App.config'] * '/'
Upvotes: 11