test[:foo] and Map.has_key?(map, :foo) are not used for the same reason in my opinion.
In the first case, you also need to check if test[:foo] is nil or not in order to determine if the map has the key
I would benchmark Map.get/2 instead of Map.has_key?/2 if i wanted to compare with test[:foo]
You are right, but this real case is a parameter received on a component, so if the key is not present I set a default, and so on. I won’t ever pass the key => nil parameter to this component.
So, I’ve tested these:
case(testmap[:foo2]) do nil -> IO.puts("Key does not exists") end
VS
case(testmap |> Map.has_key?(testmap)) do false -> IO.puts("Key does not exists") end
Results:
using Map.has_key 89.90 K 11.12 μs ±125.00% 10.04 μs 19.79 μs
using [] 86.37 K 11.58 μs ±125.56% 10.45 μs 20.64 μs
Comparison:
using Map.has_key 89.90 K
using [] 86.37 K - 1.04x slower +0.45 μs
Thanks for this tool @Ipil, maybe in this case can sound exaggerated but could be very useful in other situations.
I think you’re talking about the assigns, if so this is exactly what assign_new/3 is for
Also while we are benchmarking (which I love), I think it’s worth pointing out that you do not need to IO.puts anything in your benchmarks. You don’t really need the case either, you’re just testing map[:foo] vs. Map.has_key?(:foo), otherwise the tests are equivalent so you can strip out the rest and just test the functions you care about, e.g. fn -> map[:foo] end. On my machine the code runs >50 times faster after stripping out the IO.
And this code testmap |> Map.has_key?(testmap) is equivalent to Map.has_key?(testmap, testmap) (checking if the map has itself as a key) and will really skew your tests against testmap[:foo] as the size of your test map grows. But it’s a good philosophical question, can a map have itself as a key? Maybe the epitome of recursion if it’s an infinitely nested map.
Finally in my tests Map.get/2 beat both of these comfortably so you may consider adding that to your benchmarks. Happy benchmarking!