Slesa
1
I have read a lot of posts about mapping, but I can’t help with this.
I have database table of currencies with conversion rates for each day:
schema "fxrates" do
field :from, :string
field :of_date, :date
field :to, :string
field :value, :decimal
end
and I want to create a json with format
{
"for-date": "2025-02-14",
"rates": {
"2025-02-01": [
{"from": "US$", "to": "EUR", "value": 0.9},
{"from": "EUR", "to": "US$", "value": 1.2}
],
"2025-02-01": [
{"from": "US$", "to": "EUR", "value": 0.9},
{"from": "EUR", "to": "US$", "value": 1.2}
],
.. .
}
}
What I’ve found so far is
fxrates = Currencies.list_fxrates()
|> Enum.group_by(fn x -> x.of_date end)
|> Enum.map(fn {x,y} -> {x, Dict.values y} end)
but the mapping does not work at all. I think I lack an understanding of how grouping works and how to convert it.
@Slesa
Could this be what you are looking for?
fxrates =
[
%{from: "US$", to: "EUR", value: Decimal.new("0.9"), of_date: ~D[2025-02-01]},
%{from: "EUR", to: "US$", value: Decimal.new("1.2"), of_date: ~D[2025-02-01]},
%{from: "US$", to: "EUR", value: Decimal.new("0.9"), of_date: ~D[2025-02-01]},
%{from: "EUR", to: "US$", value: Decimal.new("1.2"), of_date: ~D[2025-02-01]}
]
rates =
fxrates
|> Enum.group_by(& &1.of_date)
|> Map.new(fn {date, rates} ->
{Date.to_string(date), Enum.map(rates, fn rate -> Map.take(rate, [:from, :to, :value]) end)}
end)
JSON.encode!(%{
"for-date" => "2025-02-14",
"rates" => rates
})
It produces the following JSON:
{
"for-date": "2025-02-14",
"rates": {
"2025-02-01": [
{
"value": "0.9",
"from": "US$",
"to": "EUR"
},
{
"value": "1.2",
"from": "EUR",
"to": "US$"
},
{
"value": "0.9",
"from": "US$",
"to": "EUR"
},
{
"value": "1.2",
"from": "EUR",
"to": "US$"
}
]
}
}
2 Likes
Slesa
3
Yes, exactly! Thanks a lot!
So the grouping creates already the list of rates.
You might want to use IO.inspect
or dbg
in your code so you can see intermediate results. Helps a lot with iterating on code.
2 Likes
Slesa
5
Final solution so far (slight change in the answer syntax):
defmodule FxRateReply do
@derive Jason.Encoder
defstruct from_currency_code: :string, to_currency_code: :string, value: :decimal
defimpl Jason.Encoder do #, for: FxRateEntry do
@impl Jason.Encoder
def encode(value, opts) do
Jason.Encode.map(%{
fromCurrencyCode: Map.get(value, :from_currency_code),
toCurrencyCode: Map.get(value, :to_currency_code),
value: Map.get(value, :value, 1.0) |> Decimal.to_string()
}, opts)
end
end
end
def all_rates(conn, _params) do
fxrates = Currencies.list_fxrates()
|> Enum.group_by(fn x -> x.of_date end)
|> Enum.map(fn {date, rates} -> %{
asOfDate: Date.to_string(date),
fxRateSource: "Elixir source",
rates: Enum.map(rates, fn rate -> %FxRateReply{
from_currency_code: Map.get(rate, :from),
to_currency_code: Map.get(rate, :to),
value: Map.get(rate, :value)
} end)
} end)
json(conn, fxrates)
end
Probably I can throw away the Jason encoder and write it directly as mentioned above.
And now I am trying to get only requested dates/currency combinations…