ok, so let’s start from your input:
iex> string = "a 1 b 2"
"a 1 b 2"
Now let’s split said string
:
iex> flat_list = String.split(string)
["a", "1", "b", "2"]
Look that we cannot convert a flat list
to map
:
iex> chunked_list = Enum.chunk_every(flat_list, 2)
[["a", "1"], ["b", "2"]]
Great! Now we can convert it to map
, but not directly. Look that each element is a list
and not tuple
. To fix that we need to pass an extra function to Map.new/2
:
iex> Map.new(chunked_list, fn [key, value] -> {key, value} end)
%{"a" => "1", "b" => "2"}
Look that here we have string
as a map
's key
. That’s more safe if we work on a data from untrusted source. However we can also change said string to atom
:
iex> Map.new(chunked_list, fn [key, value] -> {String.to_atom(key), value} end)
%{a: "1", b: "2"}
If said atoms already exists it’s recommended to use String.to_existing_atom/1
instead:
iex> Map.new(chunked_list, fn [key, value] -> {String.to_existing_atom(key), value} end)
%{a: "1", b: "2"}
Let’s take it into pipe:
defmodule Example do
def sample(string, func \\ &Function.identity/1) do
string
|> String.split()
|> Enum.chunk_every(2)
|> Map.new(fn [key, value] -> {func.(key), value} end)
end
end
iex> string = "a 1 b 2"
"a 1 b 2"
iex> Example.sample(string)
%{"a" => "1", "b" => "2"}
iex> Example.sample(string, &String.to_atom/1)
%{a: "1", b: "2"}
iex> Example.sample(string, &String.to_existing_atom/1)
%{a: "1", b: "2"}
Helpful resources:
- String.split/1
- Enum.chunk_every/2
- Map.new/2
- String.to_atom/1
- String.to_existing_atom/1
- Function.identity/1
- & (capture operator)
Note: This is the simplest way. However you can make it more efficient. As a learning exercise try to write a simple parser using:
-
Patterns and Guards with an
acc
i.e. map
accumulator passed to function
-
Map.put_new/3 for putting parsed
key
and value
into acc
# …
def sample(pattern, acc \\ %{}) do
# …