Reputation: 3221
I have a function:
def listAll(dataSchema, fields) do
dataSchema |> order() |> Repo.all()
end
that returns a query result that looks like this:
[%Skeleton.Data.Evento{__meta__: #Ecto.Schema.Metadata<:loaded, "eventos">,
date: "dewfwef", date_de: nil, date_es: nil, date_fr: nil, date_pt: nil,
id: nil, inserted_at: nil, text: "hello", text_de: nil,
text_es: nil, text_fr: "Bonjour", text_pt: "Olá", title: "Good morning", title_de: nil, title_es: nil, title_fr: nil, title_pt: "Bom dia"},
%Skeleton.Data.Evento{__meta__: #Ecto.Schema.Metadata<:loaded, "eventos">,
date: "ds", date_de: nil, date_es: nil, date_fr: nil, date_pt: nil, id: nil,
inserted_at: nil, text: "Bye", text_de: nil, text_es: nil,
text_fr: nil, text_pt: "Adeus", title: "Good evening", title_de: nil, title_es: nil, title_fr: nil, title_pt: "Boa noite"}]
fields
is a list that can contain any number of keys:
fields = [:date_pt, :text_pt, :title_pt]
How to replace all the values of date, text, title
(the same key name but without suffix) by the values of text_pt
(or whatever key/keys passed in the fields
argument) automatically?
Note that we don't know upfront which keys we have as they are passed as arguments on a case by case situation and that I would like to do this in the Ecto query, not in the resulting list of structs.
Upvotes: 0
Views: 543
Reputation: 222388
Here's how I'd do this, assuming fields contains a locale
key with a string, e.g. "pt"
:
def listAll(dataSchema, %{locale: locale}) do
text_field = :"text_#{locale}"
dataSchema
|> select([m], %{m | text: field(m, ^text_field)})
|> order()
|> Repo.all()
end
This will replace the value of text
with the value of the dynamically calculated field text_field
.
Note: You should make sure the locale
is validated at some point with a whitelist. If the user can send arbitrary locales, creating that atom can crash the VM. That : interpolation is equivalent to String.to_atom("text_#{locale}")
.
For the edited question: the function's API looks weird to me. There is probably a way better approach to do what you want to achieve. But if you really need to do it this way, here's the simplest way I can think of right now:
def listAll(dataSchema, fields) do
dataSchema
|> select([m], %{m | text: field(m, ^text_field)})
|> order()
|> Repo.all()
|> Enum.map(fn record ->
Enum.reduce(fields, record, fn field, record ->
# You might want to replace the Regex with something faster like `:binary.match` + `:binary.part` since you only need to find a '_' and remove everything after that.
without_suffix = field |> Atom.to_string |> String.replace(~r/_.*/, "") |> String.to_existing_atom
Map.put(record, without_suffix, Map.get(record, field))
end)
end)
end
Upvotes: 1