Convert input string from the prompt to map elixir?

I’m taking input from the console and I’m using prompt for that

Prompt.text(“Insert the record”) and I’m inserting this %{a: 1, b: 2}

But promp.text function will give this in a string

something like this

"%{a: 1, b: 2}"

I want to remove this string so that I can use this map. Is it possible?

another option I want to try is this

I can pass the string from the prompt like this

"a 1 b 2"

and somehow convert this to a map?

1 Like

Maybe Code.eval_string/1?

1 Like

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:

  1. String.split/1
  2. Enum.chunk_every/2
  3. Map.new/2
  4. String.to_atom/1
  5. String.to_existing_atom/1
  6. Function.identity/1
  7. & (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:

  1. Patterns and Guards with an acc i.e. map accumulator passed to function
  2. Map.put_new/3 for putting parsed key and value into acc
# …
def sample(pattern, acc \\ %{}) do
# …
2 Likes

I’d strongly advice against evaling user input. I’d probably use a plain data format like JSON.

3 Likes