Having problem with list pattern machine in Map

Hello, I have a basic question to improve my self in pattern machine, pleas see this map:

%{ mobile: ["mobile number"] }

if I use For loop , I will be able set this pattern

for {_key, [value]} <- convert_errors do
          IO.inspect value
end

but I can’t use that without for like this:

{_key, [value]}  =  convert_errors

why does it have this behaviour?

I have tested this yet, but I can’t do what pattern I need without for

%{
  "6f833e1f-be73-4089-91d2-5995e2ec4770": %{"time" => "1544976648"}
}

by the way, I don’t want to use any Module!

Thanks.

for iterates over a list of elements, which a plain pattern match does not do.

for a when is_atom(a) <- [:a, :b, :c], do: a
# [:a, :b, :c]

vs.

[:a | _] = [:a, :b, :c]

If you iterate over a map (similar to iterating over a keyword list) each individual element is a tuple of {key, value}, which is what you’re matching on in your for example.

2 Likes

In this line:

for {_key, [value]} <- convert_errors

there is no pattern matching taking place. The match operator is =, and that line has no = sign in it.

In this line:

{_key, [value]}  =  convert_errors

there is a match operator, but the thing on the left is a tuple and the thing on the right is a map, and a tuple can never match a map. Similarly, the list [1,2,3] can never match the tuple {1, 2, 3}.

To extract values from a map with pattern matching, you need to write:

iex(7)> map = %{ mobile: ["mobile number"] }
%{mobile: ["mobile number"]}

iex(8)> %{mobile: [x]} = map
%{mobile: ["mobile number"]}

iex(11)> x
"mobile number"
2 Likes

It should be noted, it may be some name and be changed, for example

map = %{ mobile: ["mobile number"] }

or

map = %{ name: ["name string"] }

I want to find the pattern that skips atom name like : mobile and name like this

%{_x: [x]} = map

how can I create a public pattern which can handle both maps.

You can’t.

Just consider %{_ => [x]} were a valid pattern, how would you match %{foo: [:bar], baz: [:quux]}?

1 Like

@NobbZ, thank you , I don’t know how it works, for the sake of this I’m just asking.

I have edited my code like this:

%{_ => value} = %{"mobile" => ["mobile number"] } 

but I have this error:

** (CompileError) iex:2: cannot use variable _ as map key inside a pattern. Map keys in patterns can only be literals (such as atoms, strings, tuples, etc) or an existing variable matched with the pin operator (such as ^some_var)
    (stdlib) lists.erl:1263: :lists.foldl/3

I’m feeling I have no control on my code, I need to have key and value in pattern just for learning.

I have this map in my code

%{"mobile" => ["mobile number"], "family" => ["mobile number"], "name" => ["mobile number"] } 

I assumed if it works, will be helpful for me

Because its not a valid pattern, I used a conjunctive form. A short sentence that should make you clear, that if and only if %{_ => value} were (but it actually is not) valid, then you had a problem with values like %{foo: [:bar], baz: [:quux]}.

1 Like

Thank you, I understood what you was saying, then at last what is your suggestion to handle these maps? using elixir module or for loop to get one map not maps !!?

I usually use Enum or Stream to process such data.

But that totally depends on what kind of processing you want to do.

1 Like

I’m a bit confused here. You say you want to improve your pattern matching skills, but you also seem to have a very specific set of constraints (has to be a for comprehension and the map values have to be a List, (and you don’t want to use any module – which I understand even less)) and a very specific data structure as if you were working on a real problem.

If I’m guessing right, this might be helpful to you

for {_key, values} <- convert_errors, is_list(values), value <- values do
  IO.inspect(value)
end

This takes advantage of a lesser known part of for comprehensions called filters. The is_list(values) is the filter that only considers lists. Then after that, we further look at each element of the list.

4 Likes

Thank you for your commenting, Actually I’m trying to improve myself and translate some elixir documents to Persian, For this purpose I saw this problem in my work.
thank you all I will use all of your hints.

See

for {_key, value} <- convert_errors do
  IO.inspect value
end

For each tuple {_key, value} which is an element of the collection/enumeration named convert_errors

{_key, [value]}  =  convert_errors

Match the (single) value named convert_errors with the tuple pattern {_key,[value]} pattern, whereupon a successful match the value is destructured according to the pattern and the subvalues are bound to the names _key and value (the leading underscore in _key indicating that the contents bound to the name _key will not be used any further).

3 Likes

i have list =[“admission fee”, “sports fee”], and I want list1 = [title: “admission fee”, title: “sports fee”]

Hello and welcome, You can do it like this…

iex> list = ["admission fee", "sports fee"]
["admission fee", "sports fee"]
iex> Enum.map(list, & {:title, &1})        
[title: "admission fee", title: "sports fee"]
1 Like