Comment joindre des chaînes dans Elixir?

158

Comment joindre deux chaînes dans une liste avec un espace, comme:

["StringA", "StringB"]

devient

"StringA StringB"
thiagofm
la source

Réponses:

220

Si vous souhaitez simplement rejoindre une liste arbitraire:

"StringA" <> " " <> "StringB"

ou utilisez simplement l'interpolation de chaîne:

 "#{a} #{b}"

Si la taille de votre liste est arbitraire:

Enum.join(["StringA", "StringB"], " ")

... toutes les solutions ci-dessus reviendront

"StringA StringB"
thiagofm
la source
36
La syntaxe alternative utilisant l'opérateur de pipeline: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell
11
Vous devez éviter l'opérateur de pipeline lorsque vous n'avez pas réellement besoin de diriger les opérations.
Carlos
3
@EdMelo Care pour expliquer pourquoi? Techniquement, vous n'avez jamais vraiment "besoin" de diriger les opérations, car le même comportement pourrait être obtenu en imbriquant des appels de fonction.
Schrockwell
8
@Schrockwell ouais, "devrait" était trop. Ce que je veux dire, c'est que dans ce cas, vous n'avez aucun gain en lisibilité, donc un simple appel de fonction rendrait thinks plus explicite.
Carlos
3
Vous devez utiliser autant de langage Elixir que possible afin de démontrer aux employeurs potentiels que vous le connaissez. J'utiliserais donc toutes les solutions ci-dessus dans le même fichier.
rodmclaughlin
61

Si ce que vous avez est une liste arbitraire, vous pouvez l'utiliser Enum.join, mais si c'est pour seulement deux ou trois, la concaténation de chaînes explicite devrait être plus facile à lire

"StringA" <> " " <> "StringB"

Cependant, souvent vous n'avez pas besoin de l'avoir sous forme de chaîne unique en mémoire si vous voulez le sortir par exemple via le réseau. Dans ce cas, il peut être avantageux d'utiliser une liste iolist (un type spécifique de liste détaillée), qui vous évite de copier des données. Par exemple,

iex(1)> IO.puts(["StringA", " ", "StringB"])
StringA StringB
:ok

Puisque vous auriez ces chaînes en tant que variables quelque part, en utilisant une liste complète, vous évitez d'allouer une toute nouvelle chaîne juste pour la sortir ailleurs. De nombreuses fonctions dans elixir / erlang comprennent les iolists, vous n'avez donc souvent pas besoin de faire le travail supplémentaire.

Carlos Martín Nieto
la source
Si vous avez besoin d'ajouter quelque chose à la fin d'une commande pipe "String" |> (& (& 1 <> "\ n")). ()
hwatkins
9

En réponse à l'exhaustivité, vous pouvez également utiliser l' interpolation de chaîne :

iex(1)> [a, b] = ["StringA", "StringB"]
iex(2)> "#{a} #{b}"
"StringA StringB"
Sheharyar
la source
5

Si vous êtes d'accord pour ajouter un espace dans votre liste, vous pouvez le traiter comme une iolist:

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

Cela vous donne des améliorations de performances car vous ne dupliquez aucune des chaînes en mémoire.

Uri
la source
4

Un Enum.reduce fonctionnerait aussi pour votre exemple non?

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

Faible Kian Seong
la source
Oui, mais il faut un Enum.reduce inversé (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c "
Andrei Sura
Je pense personnellement que c'est la meilleure réponse car elle se généralise à d'autres cas où la réduction peut être utilisée. Parle de l'idée de "do.call" dans R.
Thomas Browne
3

Cela dépend de ce que vous essayez de faire. Si vous essayez simplement d'écrire dans une nouvelle variable, utilisez simplement soit:

  • Interpolation de chaîne

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • Concatentation de chaînes: "StringA" <> " " <> "StringB

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

Cependant, comme Uri l'a mentionné, les LIO peuvent également être utilisées:

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

Les LIO seront en fait les plus performants si vous devez vous soucier de la consommation des ressources. Big Nerd Ranch a une bonne description des gains de performance avec les IOLists.

Jason Steinhauser
la source
2

Il existe un certain nombre de méthodes, mais savoir comment il gère les valeurs nulles peut déterminer la méthode à choisir.

Cela lancera une erreur

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)

Cela insérera simplement une chaîne vide "":

iex(1)> "my name is #{nil}"
"my name is "

De même que ceci:

iex(3)> Enum.join(["my name is", nil], " ")
"my name is "

Considérez également les types. Avec <>vous ne recevez aucun casting gratuit:

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"

Les performances en pratique semblent à peu près les mêmes:

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}

Donc, cela dépend vraiment si vous voulez planter ou non lorsque les valeurs interpolées sont nilou du mauvais type.

Atomkirk
la source
0

Tu pourrais aussi faire 'string A' ++ ' ' ++ 'string B'

Vatsala
la source
7
Ne deviendront-ils pas char-list?
Virtuel
0

Pensez à utiliser une liste IO, si vous avez ["String1", "string2"] et que vous utilisez iolist_to_binary / 1 dessus, vous copiez ces chaînes dans une nouvelle chaîne. Si vous avez une liste d'E / S, vous pouvez simplement la sortir dans la plupart des cas et elle la concaténera sur le port. Et c'est la chose clé, le runtime n'aura pas besoin de faire des copies des données donc c'est beaucoup plus efficace que la concaténation.

Zachary K
la source