Rich_Morin

Rich_Morin

Formatting a list of strings - am I missing anything?

I recently wrote some Elixir to format a list of strings (eg, author names) for output. I think it’s reasonably idiomatic, but I probably missed a few tricks. Suggestions, anyone?

-r

It should work like this…

iex(0)> list_str []    
""
iex(1)> list_str [1]
1
iex(2)> list_str [1,2]
"1 and 2"
iex(3)> list_str [1,2,3] 
"1, 2, and 3"

And now, the code… (ducks)

@doc """
Join a list of strings into a (mostly) comma-delimited string.
"""

def list_str( [] ), do: ""

def list_str( [ one ] ), do: one

def list_str( [ one, two ] ), do: "#{ one } and #{ two }"

def list_str(inp_list) do
  [ last | base_list ]  = Enum.reverse(inp_list)

  base_str  = base_list
  |> Enum.reverse()
  |> Enum.join(", ")

  "#{ base_str }, and #{ last }"
end

Most Liked

blatyo

blatyo

Conduit Core Team

Should probably be:

def list_str( [ one ] ), do: to_string(one)

Otherwise, looks fine to me.

blatyo

blatyo

Conduit Core Team

They should be the same. I personally tend to use to_string instead of interpolation, when it’s only the value that I’m converting to a string. I’d use interpolation when I want to convert the values to a string and concatenate with something else.

peerreynders

peerreynders

When it comes to serial commas the comma before the “and” is optional - it just can’t be there if you have less than three items.

So I guess recursion isn’t idiomatic?

  def list_str([]),
    do: ""

  def list_str([one]),
    do: to_string(one)

  def list_str(list),
    do: list_str(list, [])

  defp list_str([], [h, n | t]),
    do: list_to_str(t, to_string(n) <> " and " <> to_string(h))

  defp list_str([h | t], rest),
    do: list_str(t, [h | rest])

  defp list_to_str([], str),
    do: str

  defp list_to_str([h | t], str),
    do: list_to_str(t, to_string(h) <> ", " <> str)

Rich_Morin

Rich_Morin

Thank you for that tour de force, Peer. However, it doesn’t produce the requested result, so a picky client might complain and a picky test suite would certainly fail. Do you have a version that would pass?

As I’m sure you realize, omission of the Oxford comma can lead to ambiguity. To cite a well known example, “I’d like to thank my parents, God and Mother Teresa…” So, pedants like me don’t consider it to be optional. (Then again, I also use a lot of optional parentheses and spaces in my code…)

Leaving that issue aside, I wonder about the use of <>. Is this version

to_string(n) <> " and " <> to_string(h)

actually better for some reason (e.g., faster) than this version?

 "#{ n } and #{ h }"

Finally, although your recursive approach is most impressive, does it have any other benefits (aside from its stunning clarity and simplicity) that you can suggest? Inquiring gnomes need to mine…

yawaramin

yawaramin

If you’re outputting this with e.g. IO.puts or send_resp in Phoenix, you don’t have to output interpolated strings as results, you can output iolists. That’s the main trick that I would use. The second trick is to try to unify the handling of ‘x and y’ and ‘x, y, and z’ by realizing that the latter case reduces to the former case if you see that ‘x, y,’ in the latter corresponds to ‘x’ in the former. My version:

defmodule Test do
  def list_str([]), do: ""
  def list_str([x]), do: Integer.to_string(x)
  def list_str([x1, x2]) when is_integer(x1) do
    list_str([[Integer.to_string(x1), " "], x2])
  end
  def list_str([x1, x2]), do: [x1, "and ", Integer.to_string(x2)]
  def list_str(xs) do
    {init, [last]} = Enum.split(xs, -1) # Traverses twice
    list_str([Enum.map(init, &append_comma/1), last])
  end

  defp append_comma(x), do: [Integer.to_string(x), ", "]
end

[] |> Test.list_str |> IO.puts
[1] |> Test.list_str |> IO.puts
[1, 2] |> Test.list_str |> IO.puts
[1, 2, 3] |> Test.list_str |> IO.puts

Where Next?

Popular in Questions Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
Phillipp
Hey, I have a NanoPi-M3 and try to install Elixir on their Ubuntu image. I followed the Raspberry Pi installation instructions from the ...
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New

We're in Beta

About us Mission Statement