erlangelixirjiffy

How to convert Erlang object structure to Elixir Map?


I am using couchbeam to contact CouchDB from Elixir.

But the lib gives me back old erlang object representation like {[{"foo", "bar"}]} and not elixir maps, this was due to the lib using jiffy:decode without return_maps, How do I convert this object structure to Elixir maps (and vice versa)?

I found a hackish way to jiffy:encode and jiffy:decode it again with return_maps ... But there must be another alternative?

Update:

From Hynek's example in erlang, This seems to work:

defmodule ToMaps do

  def convert({x}) when is_list(x) do
    Map.new(x, fn {k, v} -> {k, convert(v)} end)
  end

  def convert([head | tail]) do
    [convert(head) | convert(tail)]
  end

  def convert(x) do
    x
  end
end

Seems to do the job.

iex(1)> ToMaps.convert({[{"foo",[{[{"a",1}]},3]},{"bar","baz"}]})

%{"bar" => "baz", "foo" => [%{"a" => 1}, 3]}

Solution

  • I don't know Elixir but in Erlang:

    -module(to_maps).
    
    -export([to_maps/1]).
    
    to_maps({L}) when is_list(L) ->
        maps:from_list([{K, to_maps(V)} || {K, V} <- L]);
    to_maps([H|T]) ->
        [to_maps(H) | to_maps(T)];
    to_maps(X) -> X.
    

    Edit:

    Reverse:

    from_maps(#{} = M) ->
        F = fun(K, V, Acc) -> [{K, from_maps(V)} | Acc] end,
        {maps:fold(F, [], M)};
    from_maps([H|T]) ->
        [from_maps(H) | from_maps(T)];
    from_maps(X) -> X.
    

    I would not recommend it but It can be even one function:

    convert({L}) when is_list(L) ->
        maps:from_list([{K, convert(V)} || {K, V} <- L]);
    convert(#{} = M) ->
        F = fun(K, V, Acc) -> [{K, convert(V)} | Acc] end,
        {maps:fold(F, [], M)};
    convert([H|T]) ->
        [convert(H) | convert(T)];
    convert(X) -> X.
    

    Usage:

    1> jiffy:decode(<<"{\"foo\":[3, {\"a\":1}], \"bar\":\"baz\"}">>).
    {[{<<"foo">>,[3,{[{<<"a">>,1}]}]},{<<"bar">>,<<"baz">>}]}
    2> to_maps:to_maps(v(-1)).
    #{<<"bar">> => <<"baz">>,<<"foo">> => [3,#{<<"a">> => 1}]}
    3> to_maps:from_maps(v(-1)).
    {[{<<"foo">>,[3,{[{<<"a">>,1}]}]},{<<"bar">>,<<"baz">>}]}
    4> to_maps:convert(v(-1)).
    #{<<"bar">> => <<"baz">>,<<"foo">> => [3,#{<<"a">> => 1}]}
    5> to_maps:convert(v(-1)).
    {[{<<"foo">>,[3,{[{<<"a">>,1}]}]},{<<"bar">>,<<"baz">>}]}
    6> to_maps:convert(v(-1)).
    #{<<"bar">> => <<"baz">>,<<"foo">> => [3,#{<<"a">> => 1}]}
    ...