hauks96
Building map from list of columns and list of values
I’m trying to implement an efficient generic function to map the results from a Postgrex call into a map. Any suggestions?
- Example result from postgrex:
%Postgrex.Result{
columns: ["first_column", "second_column"],
command: :select,
num_rows: 4,
rows: [["a", 1], ["b", 2], ["c", 3], ["d", 4]]
}
- Desired output
[
%{first_column: "a", second_column: 1},
%{first_column, "b", second_column: 2},
%{first_column, "c", second_column: 3},
%{first_column, "d", second_column: 4}
]
Marked As Solved
benwilson512
No worries about being a noob, but as a general note for future questions it’s also best to show the code that you’ve tried so that we can help you learn.
The most succinct way to do this is with Enum.zip. Assuming you have the struct you listed as a result variable you can do:
Enum.map(result.rows, fn row ->
result.columns
|> Enum.zip(row)
|> Map.new()
end)
Also Liked
Sebb
I can only second that. To cleanse yourself from the sins of the imperative world you should reimplement the Enum functions. This is one of the exercises in the Exercism Elixir Track. This is very much recommended! Reimplmenting Enum is not easy, so there are a lot of very good execises to lead you there.
Sebb
You do not need to index the lists. They already fit together.
iex(1)> Enum.zip(["first_column", "second_column"], ["a", 1])
[{"first_column", "a"}, {"second_column", 1}]
benwilson512
I’d highly recommend starting with a simpler question and seeing if you can build it from scratch without Enum, it’ll help with those for i cobwebs. Given:
columns = ["first_column", "second_column"]
values = ["a", 1]
How would you build a function that returned:
[{["first_column", "a"}, {"second_column", 1}]
without using Enum or List or anything like that? (Hint, recursion!)
Sebb
What have you tried?
Eiji
Here are 2 examples:
defmodule Example do
def sample1(%Postgrex.Result{columns: columns, rows: rows}) do
Enum.map(rows, fn row ->
Enum.zip_reduce(columns, row, %{}, &Map.put(&3, String.to_atom(&1), &2))
end)
end
def sample2(%Postgrex.Result{columns: _columns, rows: []}), do: []
def sample2(%Postgrex.Result{columns: columns, rows: [row | rows]}) do
[sample2(columns, row, %{}) | sample2(%Postgrex.Result{columns: columns, rows: rows})]
end
defp sample2([], [], acc), do: acc
defp sample2([column | columns], [value | row], acc) do
acc = Map.put(acc, String.to_atom(column), value)
sample2(columns, row, acc)
end
end
The first example is really short as it uses a helper Enum functions. On the other hand the second example uses just a simple pattern-matching.
Helpful resources:
- Enum.map/2
- Enum.zip_reduce/4
- Map.put/3
- String.to_atom/3
- Kernel.SpecialForms.&/1 (Capture operator)
- Patterns and Guards
Please keep in mind below every time you want to convert String to Atom:
Warning: this function creates atoms dynamically and atoms are not garbage-collected. Therefore,
stringshould not be an untrusted value, such as input received from a socket or during a web request. Consider usingto_existing_atom/1instead.







