Replace keys from list of maps

I have a list of tuple like this

  tuple =   [{"id", "provider_id"}, {"cost", "price"}] .  

And i have list of maps:

    list_of_maps =[
    %{cost: 211.0, id: 1},
    %{cost: 149.0, id: 2},
    %{cost: 247.0, id: 3},
    %{cost: 137.0, id: 4},
    %{cost: 272.0, id: 5},
    %{cost: 150.0, id: 6}
   ]

I want to replace id with provider_id and cost with price by using the the list of tuples in this map.

How can it be done?

Thanks

To solve (any) issue the best is to divide it into smaller steps.
I’ve created a function replacement_for which returns the corresponding string from tuple, ie. for :id it will return "provider_id" and so on.
Then, the function replace_keys which replaces the keys for the single map.

defmodule R do
  defp replacement_for(key, tuple) do
    tuple
    |> Enum.find(fn {x, _} -> x == to_string(key) end)
    |> elem(1)
  end

  def replace_keys(map, tuple) do
    for {k, v} <- map, into: %{}, do: {replacement_for(k, tuple), v}
  end
end

All you need to do now, is just use the replace_keys in Enum.map (or for comprehension, as you wish):

Enum.map(list_of_maps, fn m -> R.replace_keys(m, tuple) end)

Thanks

Is the key mapping something that needs to change dynamically, because can be done like so:

for %{id: id, cost: cost} <- list_of_maps, do: %{provider_id: id, price: cost}

@script: Here is another and also shortest solution I know about:

Enum.map(list_of_maps, & %{price: &1.cost, provider_id: &1.id})

For more information, see:

  1. Kernel.SpecialForms.&/1
  2. Enum.map/2

FYI: This topic is a continuation of Why doesn't ecto support dynamic map query

In particular:

I want dynamic solution.

otherwise

query = from p in Qber.V1.JobModel, where: p.id == 1, select: %{provider_id: p.id, price: p.cost}

would have been an acceptable solution in the first place.


Another alternative:

defmodule Demo do

  # 1) "tuple" is already in a format ready to
  #    be converted to a Map which can be used repeatedly
  #    over multiple rows/maps for the purpose of a key lookup
  # 2) Return a function that "knows" the key mappings
  #    via the to_new_key map in the function closure.
  # 3) We're avoiding turning strings into atoms as
  #    atoms aren't garbage collected. So we prefer
  #    ways where we turn atoms into strings.
  #    Note: Kernel.to_string/1 relies on the
  #          String.Chars protocol.
  #          If we know that we will always be using
  #          atoms, Atom.to_string seems more appropriate.
  # 4) We are not really "replacing" keys. We are only
  #    selecting values found under a key specified
  #    in the to_new_key lookup - and only if we find
  #    it, do be store it in new_map.
  #    4a) the original key is found, the value is stored
  #        under new_key in new_map.
  #    4b) the original key IS NOT FOUND
  #        SO THE value IS IGNORED - we simply
  #        return the existing new_map.
  #        FOR "replacement" we would either need
  #            Map.put(new_map, key, value)
  #        or possibly
  #            Map.put(new_map, Atom.to_string(key), value)
  #
  def make_mapper(key_list) do
    to_new_key = Map.new(key_list)

    fn ({key, value}, new_map) ->
      case Map.fetch(to_new_key, Atom.to_string(key)) do
        {:ok, new_key} ->
          Map.put(new_map, new_key, value) # (4a)
        _ ->
          new_map                          # (4b)
      end
    end
  end

  def transform(row_list, key_list) do
    #
    # 5) create key_mapper function to reuse on each map/row in row_list
    #
    key_mapper = make_mapper(key_list)
    for row <- row_list,
      do: Enum.reduce(row, %{}, key_mapper)
  end

end

tuple =   [{"id", "provider_id"}, {"cost", "price"}]
list_of_maps =[
  %{cost: 211.0, id: 1},
  %{cost: 149.0, id: 2},
  %{cost: 247.0, id: 3},
  %{cost: 137.0, id: 4},
  %{cost: 272.0, id: 5},
  %{cost: 150.0, id: 6}
]

result = Demo.transform(list_of_maps, tuple)

IO.inspect(result)
$ elixir demo.exs
[
  %{"price" => 211.0, "provider_id" => 1},
  %{"price" => 149.0, "provider_id" => 2},
  %{"price" => 247.0, "provider_id" => 3},
  %{"price" => 137.0, "provider_id" => 4},
  %{"price" => 272.0, "provider_id" => 5},
  %{"price" => 150.0, "provider_id" => 6}
]
$