Key transformation after decoding json in Elixir

In a previous blog post we saw how to do case insensitive retrieval from maps. A better solution for this if there are many key lookups is to transform the input by lower casing all the keys just after decoding. The solution from the blog post would iterate over each {key, value} pair till it found the desired key. However a proper map lookup doesn’t iterate over the keys but uses a hashing algorithm to get to the key’s location in constant time regardless of the size of the map.

Anyway, Here is the solution to transform each key for input JSON. Hope you find it useful :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
defmodule KeyTransformer do
def lower_case_keys(input) do
transform_keys(input, &String.downcase/1)
end

def transform_keys(input, tx_key_fun) when is_list(input) do
Enum.map(input, fn el -> transform_keys(el, tx_key_fun) end)
end

def transform_keys(input, tx_key_fun) when is_map(input) do
Enum.reduce(input, %{}, fn {k, v}, acc ->
Map.put(acc, tx_key_fun.(k), transform_keys(v, tx_key_fun))
end)
end

def transform_keys(value, _tx_key_fun), do: value
end

ExUnit.start

defmodule KeyTransformerTest do
use ExUnit.Case
import KeyTransformer

test "simple map" do
assert lower_case_keys(%{"NAME" => "Khaja"}) == %{"name" => "Khaja"}
assert lower_case_keys(%{"NAME" => "Khaja", "Age" => 3}) == %{"name" => "Khaja", "age" => 3}
end

test "nested map" do
assert lower_case_keys(%{"Mujju" => %{"NAME" => "Khaja"}}) == %{"mujju" => %{"name" => "Khaja"}}
end

test "deeply nested map" do
assert lower_case_keys(%{"Children" => %{"Mujju" => %{"NAME" => "Khaja"}}}) == %{"children" => %{"mujju" => %{"name" => "Khaja"}}}
end

test "list of maps" do
assert lower_case_keys([%{"NAME" => "Zainu"}]) == [%{"name" => "Zainu"}]

assert lower_case_keys([%{
"NAME" => "Khaja Muzaffaruddin",
"agE" => 2,
}, %{}]) == [%{"age" => 2, "name" => "Khaja Muzaffaruddin"}, %{}]
end

test "nested list of maps" do
assert lower_case_keys(%{
"JUlian" => [%{"Movie" => "Madagascar"}]
}) == %{"julian" => [%{"movie" => "Madagascar"}]}
end

test "deeply nested list of maps" do
assert lower_case_keys(%{"MovieGenres" => [%{
"JUlian" => [%{"Movie" => "Madagascar"}]
}, %{"Ho" => 33}], "OK then" => "little story"}) == %{
"moviegenres" => [%{"julian" => [%{"movie" => "Madagascar"}]}, %{"ho" => 33}], "ok then" => "little story"
}
end
end

I am currently working on LiveForm which makes setting up contact forms on your website a breeze.