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.
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.
One option for deterministic behaviour is to sort the keys before picking the first one to use for the delete.
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.
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?
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.
OP doesn’t seem to care that they are out of order but I’m dying to know the use-case
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.
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.
I think we all are which is why this thread is still alive
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"]
Subscribed…
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.
Hey, same with Ruby! All hashes will iterate by insertion order.