Question

How do I join two strings in a list with a space, like:

["StringA", "StringB"]

becomes

"StringA StringB"
Was it helpful?

Solution

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"

OTHER TIPS

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"

How each method handles nil

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.

It depends on what you're trying to do. If you're just trying to write out to a new variable, then just use either:

  • String interpolation

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • String concatentation: "StringA" <> " " <> "StringB

  • Enum.join(): ["StringA", "StringB"] |> Enum.join(" ")

However, as Uri mentioned, IOLists can also be used:

["StringA", " ", "StringB"] |> IO.iodata_to_binary

IOLists are actually going to be the most performant if you need to care about resource consumption. Big Nerd Ranch has a good write-up on performance gains w/ IOLists.

If you were OK with adding a space in your list you could treat it as an iolist:

["StringA", " ", "StringB"] |> IO.iodata_to_binary # "StringA StringB"

This gives you some performances boosts as you're not duplicating any of the strings in memory.

An Enum.reduce would work too for your example no ?

iex(4)> Enum.reduce(["StringA", "StringB"], fn(x, acc) -> x <> " " <> acc end) "StringB StringA"

Consider using an IO List, if you have ["String1", "string2"] and you use iolist_to_binary/1 on it then you will copy those strings into a new string. If you have an IO list you can just output it in most cases and it will concatenate it on the port. And this is the key thing, the runtime will not need to make copies of the data so it is much more efficient than concatenation.

You could also do 'string A' ++ ' ' ++ 'string B'

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top