Geo
Geo

Reputation: 96827

What is wrong with this elixir map example?

I would have imagine that the following would work, based on articles I found online:

iex(9)> q
%{one: 1, two: 2}
iex(10)> nq = %{ q | three: 4}   
** (KeyError) key :three not found in: %{one: 1, two: 2}
    (stdlib) :maps.update(:three, 4, %{one: 1, two: 2})
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1262: :lists.foldl/3
iex(10)> nq = %{ q | :three => 4}
** (KeyError) key :three not found in: %{one: 1, two: 2}
    (stdlib) :maps.update(:three, 4, %{one: 1, two: 2})
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1262: :lists.foldl/3

I was trying to add a new element to the map. Can anyone tell me what I'm doing wrong? I'm running the following:

Erlang/OTP 18 [erts-7.2] [source-e6dd627] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.3.0-dev) - press Ctrl+C to exit (type h() ENTER for help)

Upvotes: 3

Views: 1064

Answers (2)

justin
justin

Reputation: 3607

Not what you're asking, but thought I'd post one way (which I guess is short enough) to expand on this to work with several Maps:

iex(1)> q = %{one: 1, two: 2}
%{one: 1, two: 2}
iex(2)> b = %{three: 3, four: 4}
%{four: 4, three: 3}
iex(3)> Enum.reduce([q, b, %{five: 5}], &Map.merge/2)
%{five: 5, four: 4, one: 1, three: 3, two: 2}

Upvotes: 1

ham-sandwich
ham-sandwich

Reputation: 4052

The | is a map specific construct that provides the ability to update and access atom keys. Because their is no :three key in %{one: 1, two: 2}, it is throwing you an error.

What you need to use is Map.put/3.

Example

iex(1)> q = %{one: 1, two: 2}      
%{one: 1, two: 2}
iex(2)> q = Map.put(q, :three, 3)
%{one: 1, three: 3, two: 2}

Here, q is rematched with the right hand side (iex(2)).

Further

Simon St. Laurent & J. David Eisenberg in their book Introducing Elixir: Getting Started In Functional Programming.

You may also want to add another key-value pair to a map. You can't of course, change the map intself, but the Dict.put_new library function can easily create a new map that includes the original plus an extra value:

iex(1)> q = %{one: 1, two: 2} 
%{one: 1, two: 2}
iex(2)> Dict.put_new( q, :three, 3)
%{one: 1, three: 3, two: 2}
iex(3)> q = Dict.put_new( q, :three, 3)

However, even though it works this should be avoided as of Elixir 1.2 because Dict + HashDict has been deprecated. Just use the Map module functions.

Upvotes: 9

Related Questions