Maps: Access dynamic key

First post here ! :partying_face:

I’m kind of new to Elixir and I have an issue accessing a specific key when this key is a variable.
Let’s say you have:

a_map = %{name: "Martine", list: %{
    "key1" => %{a_key: "A value", another_key: "Another value"},
    "key2" => %{a_key: "A value", another_key: "Another value"}
}}

Now I want to update, or even replace, the another_key for any key in a variable:

key = "key1"
a_map = update_in(a_map[:list][key][:another_key], &(&1 <> " updated"))

This returns me function get_and_update/3 is undefined

How can I update a map in this scenario ? Do I have to use the merge map syntaxe ?

Welcome! The code works well in my iex, maybe error from some where else?

Thanks for your response !
So, yep this code works, which is surprising for me.

Here is the “real” code with the actual error.
The Struct:

%App.Games.Game{
  id: "owieruoiewurwer",
  name: "Game 1",
  players: %{
    "jwuiyeywi" => %App.Games.Player{
      admin: true,
      card: nil,
      name: "Bastien",
      role: "BA"
    },
    "oiwueroiquw" => %App.Games.Player{
      admin: false,
      card: nil,
      name: "Sylvain",
      role: "DEV"
    }
  }
}

And then:

key = "jwuiyeywi"
put_in(game[:players][key][:card], "2")

Which throw:

** (UndefinedFunctionError) function App.Games.Game.get_and_update/3 is undefined (App.Games.Game does not implement the Access behaviour)
(easy_poker_planning 0.1.0) App.Games.Game.get_and_update(%App.Games.Game{id: “owieruoiewurwer”, name: “Game 1”, players: %{“jwuiyeywi” => %App.Games.Player{admin: true, card: nil, name: “Bastien”, role: “BA”}, “oiwueroiquw” => %App.Games.Player{admin: false, card: nil, name: “Sylvain”, role: “DEV”}}}, :players, #Function<44.79398840/1 in :erl_eval.expr/5>)
(elixir 1.11.3) lib/access.ex:346: Access.get_and_update/3

You’ll want to use . with structs rather than [], something like put_in(game.players[key].card, "2")

3 Likes

I didn’t know what the Access behavior was but now it makes a lot more sense, and it works.
Thanks a lot :pray:

Anyone knows if accessing keys in maps are O(1) operation?

Yes, it is.

1 Like

I’m pretty sure it’s logarithmic.

1 Like

Small maps are ordered lists where access is O(n) (EDIT: or O(log n)?). Bigger maps (with 32+ keys) are tries with O(log n) access.

Source: https://stackoverflow.com/a/35685220

3 Likes

You are wrong. Erlang uses HAMT for large maps. Smaller maps (up to 32 elements) have logarithmic complexity, but that mean at most 5 comparisons, so we can say that these have also constant access time. More about HAMT

2 Likes

This conflicts with other information that I could find, such as the SO answer above. Can you clarify this?

I guess the ordered list can be searched through with a binary search, leading to logarithmic lookup time, but everywhere else I’ve seen the HAMT mentioned to also have logarithmic lookup.

1 Like

I got my information from the Map docs (Map — Elixir v1.16.0):

The functions in this module that need to find a specific key work in logarithmic time. This means that the time it takes to find keys grows as the map grows, but it’s not directly proportional to the map size. In comparison to finding an element in a list, it performs better because lists have a linear time complexity.

2 Likes

Totally ignoring the real implementation now, just to make sure, tries do not have a time complexity for searching that would depend on the number of entries, but instead they depend on the length of the key they are trying to access.

And all hashed keys have the same length, which mean that we have O(1) complexity.

Kind of.

That requires you to hash first, which is O(n) again.

Also original trie does not imply any hashing.

1 Like