Fun with lists of char lists

47 views
Skip to first unread message

Jim Menard

unread,
Apr 26, 2014, 2:46:22 PM4/26/14
to elixir-l...@googlegroups.com
I'm trying to turn a YAML file into a Map. The yamerl library
(https://github.com/yakaz/yamerl) returns an array of tuples where the
keys are char lists and the values are built-in values like char
lists, numbers, lists, or arrays of tuples. It's not difficult to
recurse through the data and turn things into maps.

The problem I'm having is with turning list of chars into lists of
strings. I end up turning ['foo.com', 'bar.com'] into "foo.combar.com"
and turning [1, 2, 3] into <<1, 2, 3>>.

Here's what I have so far. What am I doing wrong?

defmodule Servers.Converter do

def to_atom_map(yaml_tuples) do
_to_atom_map(yaml_tuples, %{})
end

defp _to_atom_map([], map), do: map

defp _to_atom_map([{key, val} | yaml_tuples], map) do
yaml_tuples |> _to_atom_map(Dict.put_new(map, list_to_atom(key),
to_atom_map(val)))
end

defp _to_atom_map(val, _) when is_list(val) do
case String.from_char_data(val) do
{:ok, s} -> s
_ -> val |> Enum.map &(to_atom_map(&1))
end
end

defp _to_atom_map(val, _), do: val
end

For fun, here is the unit test I have:

defmodule ConverterTest do
use ExUnit.Case

alias Servers.Converter, as: C

test "empty" do
assert C.to_atom_map([]) == %{}
end

test "get back a map with atomic keys" do
assert C.to_atom_map([{'a', 'b'}]) == %{a: "b"}
end

test "more than one key" do
assert C.to_atom_map([{'a', 'b'}, {'c', 42}]) == %{a: "b", c: 42}
end

test "convert list values" do
# This fails: I get back {a: <<1, 2, 3>>}
assert C.to_atom_map([{'a', [1, 2, 3]}]) == %{a: [1, 2, 3]}
assert C.to_atom_map([{'a', [1, [2, 3]]}]) == %{a: [1, [2, 3]]}
end

test "convert lists of strings" do
# This fails: I get back {a: "abcdef"}
assert C.to_atom_map([{'a', ['abc', 'def']}]) == %{a: ["abc", "def"]}
end

test "recursive structures mapped" do
assert C.to_atom_map([{'a', [{'b', 'c'}]}]) == %{a: %{b: "c"}}
end
end

Jim
--
Jim Menard, http://www.jimmenard.com/

José Valim

unread,
Apr 26, 2014, 4:06:15 PM4/26/14
to elixir-l...@googlegroups.com
If yamerl returns lists for [1, 2, 3] and the string "foo" then there is no way to effectively make a distinction in between them. The best approach would be to fork and add an option to yamerl to return strings as binaries in case it doesn't have such option yet.



José Valim
Skype: jv.ptec
Founder and Lead Developer


--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jim Menard

unread,
Apr 26, 2014, 5:36:04 PM4/26/14
to elixir-l...@googlegroups.com
Thanks, José. Time to take a look inside yamerl.

Jim Menard

unread,
Apr 26, 2014, 6:52:28 PM4/26/14
to elixir-l...@googlegroups.com
Solved. I ended up using yamerl's detailed_constr option, which
returns a tuple structure that includes type information. If anybody's
interested, here's what I've come up with. It returns a list of maps.

https://github.com/jimm/elixir/blob/master/yaml/yaml_to_map.ex
https://github.com/jimm/elixir/blob/master/yaml/yaml_to_map_test.exs
Reply all
Reply to author
Forward
0 new messages