112

I'm looking for a better way to merge variables into a string, in Ruby.

For example if the string is something like:

"The animal action the second_animal"

And I have variables for animal, action and second_animal, what is the prefered way to put those variables in to the string?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
James Pearson
  • 1,622
  • 2
  • 15
  • 23

7 Answers7

271

The idiomatic way is to write something like this:

"The #{animal} #{action} the #{second_animal}"

Note the double quotes (") surrounding the string: this is the trigger for Ruby to use its built-in placeholder substitution. You cannot replace them with single quotes (') or the string will be kept as is.

Chucky
  • 545
  • 7
  • 14
Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
  • 2
    Sorry, maybe I simplified the problem too much. The String will be pulled from a database, and the variable dependant a number of factors. Normally I would use a replace for 1 or two varibles, but this has the potential to be more. Any thoughts? – James Pearson Feb 16 '09 at 22:25
  • The #{} construct is probably the fastest (although that still seems counterintuitive to me). As well as gix's suggestion, you can also assemble strings with + or <<, but there may be some intermediate strings created, which is costly. – Mike Woodhouse Feb 17 '09 at 08:57
  • Best way to interpolate variables – Dragutescu Alexandru Mar 29 '16 at 08:32
  • what does *idiomatic* means? – Udesh Jun 13 '23 at 04:03
  • "idiomatic": following the conventions of the language, writing code using the features of the language, rather than trying to force it into looking like another language. – Mike Woodhouse Jun 16 '23 at 15:31
126

You can use sprintf-like formatting to inject values into the string. For that the string must include placeholders. Put your arguments into an array and use on of these ways: (For more info look at the documentation for Kernel::sprintf.)

fmt = 'The %s %s the %s'
res = fmt % [animal, action, other_animal]  # using %-operator
res = sprintf(fmt, animal, action, other_animal)  # call Kernel.sprintf

You can even explicitly specify the argument number and shuffle them around:

'The %3$s %2$s the %1$s' % ['cat', 'eats', 'mouse']

Or specify the argument using hash keys:

'The %{animal} %{action} the %{second_animal}' %
  { :animal => 'cat', :action=> 'eats', :second_animal => 'mouse'}

Note that you must provide a value for all arguments to the % operator. For instance, you cannot avoid defining animal.

Chucky
  • 545
  • 7
  • 14
gix
  • 5,737
  • 5
  • 34
  • 40
20

I would use the #{} constructor, as stated by the other answers. I also want to point out there is a real subtlety here to watch out for here:

2.0.0p247 :001 > first_name = 'jim'
 => "jim" 
2.0.0p247 :002 > second_name = 'bob'
 => "bob" 
2.0.0p247 :003 > full_name = '#{first_name} #{second_name}'
 => "\#{first_name} \#{second_name}" # not what we expected, expected "jim bob"
2.0.0p247 :004 > full_name = "#{first_name} #{second_name}"
 => "jim bob" #correct, what we expected

While strings can be created with single quotes (as demonstrated by the first_name and last_name variables, the #{} constructor can only be used in strings with double quotes.

Starkers
  • 10,273
  • 21
  • 95
  • 158
15
["The", animal, action, "the", second_animal].join(" ")

is another way to do it.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
11

This is called string interpolation, and you do it like this:

"The #{animal} #{action} the #{second_animal}"

Important: it will only work when string is inside double quotes (" ").

Example of code that will not work as you expect:

'The #{animal} #{action} the #{second_animal}'
sqrcompass
  • 643
  • 1
  • 8
  • 17
  • Thank you for using the proper term so new programmers can learn: interpolation. –  Nov 29 '17 at 23:56
3

The standard ERB templating system may work for your scenario.

def merge_into_string(animal, second_animal, action)
  template = 'The <%=animal%> <%=action%> the <%=second_animal%>'
  ERB.new(template).result(binding)
end

merge_into_string('tiger', 'deer', 'eats')
=> "The tiger eats the deer"

merge_into_string('bird', 'worm', 'finds')
=> "The bird finds the worm"
Clint Pachl
  • 10,848
  • 6
  • 41
  • 42
0

You can use it with your local variables, like this:

@animal = "Dog"
@action = "licks"
@second_animal = "Bird"

"The #{@animal} #{@action} the #{@second_animal}"

the output would be: "The Dog licks the Bird"