How to join strings in Elixir?
If you just want to join some arbitrary list:
"StringA" <> " " <> "StringB"
or just use string interpolation:
"#{a} #{b}"
If your list size is arbitrary:
Enum.join(["StringA", "StringB"], " ")
... all of the solutions above will return
"StringA StringB"
If what you have is an arbitrary list, then you can use Enum.join
, but if it's for just two or three, explicit string concatenation should be easier to read
"StringA" <> " " <> "StringB"
However, often you don't need to have it as a single string in memory if you're going to output it through e.g. the network. In that case, it can be advantageous to use an iolist (a specific type of a deep list), which saves you from copying data. For example,
iex(1)> IO.puts(["StringA", " ", "StringB"])
StringA StringB
:ok
Since you would have those strings as variables somewhere, by using a deep list, you avoid allocating a whole new string just to output it elsewhere. Many functions in elixir/erlang understand iolists, so you often wouldn't need to do the extra work.
Answering for completeness, you can also use String interpolation:
iex(1)> [a, b] = ["StringA", "StringB"]
iex(2)> "#{a} #{b}"
"StringA StringB"
There are a number of methods, but knowing how it handles nil values can determine which method you should choose.
This will throw an error
iex(4)> "my name is " <> "adam"
"my name is adam"
iex(1)> "my name is " <> nil
** (ArgumentError) expected binary argument in <> operator but got: nil
(elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
(elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
(elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
(elixir) expanding macro: Kernel.<>/2
iex:1: (file)
This will just insert a blank "" string:
iex(1)> "my name is #{nil}"
"my name is "
As will this:
iex(3)> Enum.join(["my name is", nil], " ")
"my name is "
Also consider types. With <>
you don't get any free casting:
iex(5)> "my name is " <> 1
** (ArgumentError) expected binary argument in <> operator but got: 1
(elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
(elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
(elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
(elixir) expanding macro: Kernel.<>/2
iex:5: (file)
iex(5)> "my name is #{1}"
"my name is 1"
iex(7)> Enum.join(["my name is", 1], " ")
"my name is 1"
Performance in practice seems roughly the same:
iex(22)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8023855, :ok}
iex(23)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8528052, :ok}
iex(24)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{7778532, :ok}
iex(25)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7620582, :ok}
iex(26)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7782710, :ok}
iex(27)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7743727, :ok}
So, really depends on if you want to crash or not when the interpolated values are nil
or the wrong type.