Delete the 1st pair in a map

Hi OGs,
How can I delete the 1st pair in a map ?
Thank you

AFAIK maps are unordered, so there is no “first”. You could enumerate over it, collecting all but the first in the enumeration, but I think it would be an arbitrary selection so I’m not sure there’s much difference between that and Map.delete(map, random_key)

In this situation, my keys are atoms or lists, or at least I need to iterate through a map except 1st one, maybe something like Enum.each(map, fn key, value -> if key == Enum.at(map,0) do end end ?

map
|> Enum.to_list()
|> tl()
|> Enum.into(%{})

A simple way but maybe not most performant and doesn’t solve the problem I stated above

Please post your code showing how your map is constructed. As @msimonborg said, there is no such concept of the “first” item in a map.

6 Likes

Actually, I can do this trick [ first_key_to_delete | _] = Enum.to_list(my_map) thanks to @msimonborg

Just for the sake of experiment, try the following in iex

m1 = Enum.map(1..100, &{&1, &1}) |> Map.new()
m2 = Enum.map(1..1000, &{&1, &1}) |> Map.new()
Enum.at(m1, 0) == Enum.at(m2, 0)

There is no consistent concept of the “first” in a map, otherwise this should return true no matter how large or small you make the range. If you are trying to skip or remove the first pair of a map, you will most likely get inconsistent results as the size and composition of the map changes.

2 Likes

One option for deterministic behaviour is to sort the keys before picking the first one to use for the delete.

1 Like
map
|> Enum.sort() # use deafult erlang ordering
|> tl() # get the tail, the rest but the first
|> Map.new()

Check also Enum.sort_by/3 for custom ordering

Which key do you call the first key of a map and how you know it is still the first key ? I think that’s what the other replies are trying to tell you.

iex(13)> %{a: 1, b: 2} == %{b: 2, a: 1}
true

You can see that for Elixir even if I invert the order of the above maps there are considered to be the same map.

iex(14)> %{b: 2, a: 1}
%{a: 1, b: 2}

And above you can notice that when Elixir is evaluating %{b: 2, a: 1} it sorts the keys differently. So if one thinks that :b will always be the first key, he could be totally wrong.

I think it’s better to know the name of the key you want actually to be deleted when manipulating maps.

1 Like

Ah yep, in this case, all the keys’ properties are quite similar, and I just want to delete first one, that’s all

But how do you know which one is the “first” one?

2 Likes

If you don’t specifically need a map, consider using :gb_trees instead. Its take_smallest/1 and take_largest/1 operations are bound to be quicker than converting to list, sorting, deleting the first entry, and then creating a map again.

6 Likes

OP doesn’t seem to care that they are out of order but I’m dying to know the use-case :joy:

8 Likes

I hadn’t really thought of :gb_trees as “ordered maps” but I suppose that’s more or less what they are. Like others have said I’m curious what the OP’s actual use case is where a random key-value pair being taken out of the map is the desired behavior.

1 Like

Yeah this whole exchange is kind of wild.

@docjazither just in case this isn’t abundantly clear: What you’re doing is deleting a random (ish) key from the map. If that’s OK with you then proceed. If it isn’t, please answer one of the several people who asked you to clarify what “first” means to you.

5 Likes

I think we all are which is why this thread is still alive :sweat_smile:

The sorting solutions and :gb_trees are interesting but I suppose it assumes “first” can be determined by some sorting rules. I’ve been assuming (stress on assuming because we’d all like to know for sure) “first” means “first to be inserted”, the way you might determine the order of a list.

@docjazither If you need a key-value data structure with (atom keys and) precise ordering so that “first” can always be preserved, you might be better of with Keyword lists than with Map. If you are adding or removing elements from your map, the “first” in your enumerations will change. Just beware that Keyword.put/3 inserts new keys at the head of the list, not the tail.

%{}
|> Map.put(:one, "one")
|> Map.put(:two, "two")
|> Map.put(:three, "three")
|> Map.put(:four, "four")
# %{four: "four", one: "one", three: "three", two: "two"}

[]
|> Keyword.put(:one, "one")
|> Keyword.put(:two, "two")
|> Keyword.put(:three, "three")
|> Keyword.put(:four, "four")
|> Enum.reverse()
# [one: "one", two: "two", three: "three", four: "four"]
2 Likes

Subscribed… :joy: :popcorn:

3 Likes

fun fact: since 3.7 python dicts keep insertion order.

https://mail.python.org/pipermail/python-dev/2017-December/151283.html

No I do not think that we’d want that for Elixir.

2 Likes

Hey, same with Ruby! All hashes will iterate by insertion order.