JSON Map in Elixir
I parsed the following JSON using Posion.decode!
json = %{"color-Black|size:10" =>
%{"attributes" => %{"color" => "Black","size" => "11"},
"isAvailable" => true,
"pricing" => %{"standard" => "$415.00", "sale" => 415}},
"color|size-EU:9.5" =>
%{"attributes" => %{"color" => "Black","size" => "11"},
"isAvailable" => true,
"pricing" => %{"standard" => "$415.00", "sale" => 415}}}
I want to map this, and I cannot get the JSON elements as text in the node element. So far I have tried.
Enum.map(json , fn(item) ->
%{
color: item.attributes["color"],
size: item.attributes["size"],
price: item.pricing["standard"] * 100,
isAvailable: item.isAvailable
}
end)
But this code gives some access related error.
When matching a map, repeating key-value pairs arrive in mapper as tuples {key, value}
:
Enum.map(json, fn {_, %{"attributes" => attributes,
"isAvailable" => isAvailable,
"pricing" => pricing}} ->
%{
color: attributes["color"],
size: attributes["size"],
price: pricing["standard"],
isAvailable: isAvailable
}
end)
#โ [
# %{color: "Black", isAvailable: true, price: "$415.00", size: "11"},
# %{color: "Black", isAvailable: true, price: "$415.00", size: "11"}
# ]
Here we use inplace pattern matching for values โโin mapper to simplify the pairing code itself and make it less error prone in case of bad input.
Three things:
You have a map, so it
Enum.map
will give you a set of keys and values. You just need the value here, so:fn {_, item} ->
. . :
item["attributes"]
item.attributes
.
. Float, . ,
String.trim_leading
String.to_float
:iex(1)> "$123.45" |> String.trim_leading("$") |> String.to_float 123.45
Enum.map(json, fn {_, item} ->
%{
color: item["attributes"]["color"],
size: item["attributes"]["size"],
price: item["pricing"]["standard"] |> String.trim_leading("$") |> String.to_float |> Kernel.*(100),
isAvailable: item["isAvailable"]
}
end)
|> IO.inspect
:
[%{color: "Black", isAvailable: true, price: 4.15e4, size: "11"},
%{color: "Black", isAvailable: true, price: 4.15e4, size: "11"}]
You get access errors because you can use the syntax thing.property
if it property
is an atom and there are lines on your json card.
One thing that will make this a lot easier is that it Poison.decode
can take a second argument keys: :atoms
to return a map with atomic keys. Working with what you have here, this will solve the problem:
json_atoms = Poison.encode!(json) |> Poison.decode!(keys: :atoms)
convert_price = fn x ->
String.trim_leading(x, "$")
|> String.to_float
|> &(&1 * 100)
|> trunc
end
Enum.map(json_atoms, fn {_k,v} -> %{
color: v.attributes.color,
size: v.attributes.size,
price: convert_price.(v.pricing.standard),
isAvailable: v.isAvailable
} end)