Map.merge and Dict.merge

This works:

Dict.merge(%{a: 123}, layout: {MyApp.LayoutView, "some_page.html"})

And this throws an exception:

Map.merge(%{a: 123}, layout: {MyApp.LayoutView, "some_page.html"})
# => (BadMapError) expected a map, got: [layout: {MyApp.LayoutView, "some_page.html"}]

Athough

warning: Dict.merge/2 is deprecated, use the Map module for working with maps or the Keyword module for working with keyword lists

How to use Map if it throws an exception?

Map.merge(%{a: 123}, %{layout: {MyApp.LayoutView, "some_page.html"}})
1 Like

It says “use maps for working with maps” and “the Keyword module for working with keyword lists”, emphasis added. If you need to work with both at the same time you can consider Enum.into.

%{a: 123} |> Enum.into(layout: {MyApp.LayoutView, "some_page.html"})

And that specific example puts a map into a keyword list, thus returning:

iex> %{a: 123} |> Enum.into(layout: {MyApp.LayoutView, "some_page.html"})
[layout: {MyApp.LayoutView, "some_page.html"}, a: 123]

And of course the other way to put in to a map:

iex> [layout: {MyApp.LayoutView, "some_page.html"}] |> Enum.into(%{a: 123})
%{a: 123, layout: {MyApp.LayoutView, "some_page.html"}}

EDIT: I find it interesting that the keyword list is overriding the ‘into’ options, so into kind of does a reverse merge only on keyword lists, I think this is a bug:

iex> %{a: 42} |> Enum.into(%{a: 62})
%{a: 42}
iex> [a: 42] |> Enum.into([a: 62])
[a: 62, a: 42]
iex> [a: 42] |> Enum.into([a: 62]) |> Keyword.get(:a)
62

Yeah, into is working the wrong way on lists it seems, for maps the left overrides the right, but for lists the right is overriding the left, I see why in code, hmm… Is it a bug though, or is it by design… @josevalim ?

3 Likes

It is simple, if you add something to a list, like, for example, some configuration, you want the new thing at the end so that folding the list will make the things at the end override the ones at the beginning