Bagaimana cara bergabung dengan string di Elixir?

158

Bagaimana cara saya menggabungkan dua string dalam daftar dengan spasi, seperti:

["StringA", "StringB"]

menjadi

"StringA StringB"
thiagofm
sumber

Jawaban:

220

Jika Anda hanya ingin bergabung dengan beberapa daftar arbitrer:

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

atau cukup gunakan interpolasi string:

 "#{a} #{b}"

Jika ukuran daftar Anda arbitrer:

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

... semua solusi di atas akan kembali

"StringA StringB"
thiagofm
sumber
36
Sintaks alternatif menggunakan operator pipa: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell
11
Anda harus menghindari operator pipa ketika Anda sebenarnya tidak memiliki kebutuhan untuk operasi pipa.
Carlos
3
@EdMelo Care untuk menjelaskan mengapa? Secara teknis Anda tidak pernah benar-benar "perlu" untuk operasi pipa, karena perilaku yang sama dapat dicapai dengan panggilan fungsi bersarang.
Schrockwell
8
@ Rockwell ya, "harus" terlalu banyak. Yang saya maksudkan adalah bahwa kasus ini Anda tidak mendapatkan keterbacaan sehingga panggilan fungsi sederhana akan membuat berpikir lebih eksplisit.
Carlos
3
Anda harus menggunakan sebanyak mungkin bahasa Elixir untuk menunjukkan kepada calon atasan yang Anda kenal. Jadi saya akan menggunakan semua solusi di atas dalam file yang sama.
rodmclaughlin
61

Jika yang Anda miliki adalah daftar arbitrer, maka Anda dapat menggunakannya Enum.join, tetapi jika hanya untuk dua atau tiga, rangkaian string eksplisit harus lebih mudah dibaca

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

Namun, sering kali Anda tidak perlu memilikinya sebagai string tunggal dalam memori jika Anda akan mengeluarkannya melalui misalnya jaringan. Dalam hal ini, akan menguntungkan untuk menggunakan iolist (tipe spesifik dari daftar yang mendalam), yang menyelamatkan Anda dari menyalin data. Sebagai contoh,

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

Karena Anda akan memiliki string tersebut sebagai variabel di suatu tempat, dengan menggunakan daftar yang mendalam, Anda menghindari mengalokasikan string yang sama sekali baru hanya untuk output di tempat lain. Banyak fungsi di elixir / erlang mengerti iolists, jadi Anda seringkali tidak perlu melakukan pekerjaan ekstra.

Carlos Martín Nieto
sumber
Jika Anda perlu menambahkan sesuatu di akhir perintah pipa "String" |> (& (& 1 <> "\ n")). ()
hwatkins
9

Menjawab untuk kelengkapan, Anda juga dapat menggunakan interpolasi String :

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

Jika Anda baik-baik saja dengan menambahkan spasi di daftar Anda, Anda bisa memperlakukannya sebagai seorang iolist:

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

Ini memberi Anda beberapa peningkatan kinerja karena Anda tidak menduplikasi string dalam memori.

Uri
sumber
4

Sebuah Enum.reduce akan bekerja juga untuk contoh Anda, tidak?

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

Low Kian Seong
sumber
Ya, tetapi membutuhkan Enum.reduce terbalik (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c "
Andrei Sura
secara pribadi berpikir ini adalah jawaban terbaik karena generalisasi untuk kasus lain di mana pengurangan dapat digunakan. Berbicara tentang gagasan "do.call" dalam R.
Thomas Browne
3

Itu tergantung pada apa yang Anda coba lakukan. Jika Anda hanya mencoba menulis ke variabel baru, gunakan saja:

  • Interpolasi string

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

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

Namun, seperti yang disebutkan Uri, IOLists juga dapat digunakan:

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

IOLists sebenarnya akan menjadi yang paling berprestasi jika Anda perlu peduli dengan konsumsi sumber daya. Peternakan Big Nerd memiliki artikel bagus tentang peningkatan kinerja dengan IOLists.

Jason Steinhauser
sumber
2

Ada sejumlah metode, tetapi mengetahui bagaimana menangani nilai nil dapat menentukan metode mana yang harus Anda pilih.

Ini akan menimbulkan kesalahan

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)

Ini hanya akan memasukkan string "" kosong:

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

Seperti ini:

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

Juga pertimbangkan jenis. Dengan <>Anda tidak mendapatkan casting gratis:

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"

Kinerja dalam praktik tampaknya kurang lebih sama:

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}

Jadi, sangat tergantung pada apakah Anda ingin crash atau tidak ketika nilai yang diinterpolasi adalah nilatau tipe yang salah.

atomkirk
sumber
0

Anda juga bisa melakukannya 'string A' ++ ' ' ++ 'string B'

Vatsala
sumber
7
Bukankah mereka akan menjadi daftar karakter?
Virtual
0

Pertimbangkan untuk menggunakan Daftar IO, jika Anda memiliki ["String1", "string2"] dan Anda menggunakan iolist_to_binary / 1 di atasnya maka Anda akan menyalin string tersebut ke string baru. Jika Anda memiliki daftar IO, Anda bisa menampilkannya dalam banyak kasus dan akan menggabungkannya di port. Dan ini adalah kuncinya, runtime tidak perlu membuat salinan data sehingga jauh lebih efisien daripada penggabungan.

Zachary K
sumber