How put something in maps in list

I make a query to db and return list:

[%{meal_id: 1}, %{meal_id: 2}]

I need put something in every map. I try:

Enum.map(mymeals, fn(x) -> #enum my list
v = Map.get(x, :meal_id) #get value from meal_id (need for search something in db)

and i don’t have any idea how i can edit my maps, i have problem with immutable, i know i have to create new maps, but how make it in this Enum.map “loop”?:smiley:

Finally I want get something like:

[%{meal_id: 1, product_id: 450}, %{meal_id: 2, product_id: 330}]

You can do something like this:

Enum.map(my_meals, fn(x) -> Map.merge(x, %{product_id: 1}) end)

which will give you a map:

[%{meal_id: 1, product_id: 1}, %{meal_id: 2, product_id: 1}]
3 Likes

You can also do like this

iex(1)> mymeals = [%{meal_id: 1}, %{meal_id: 2}]
[%{meal_id: 1}, %{meal_id: 2}]
iex(2)> mymeals |> Enum.map(& Map.put(&1, :product_id, &1.meal_id * 2))
[%{meal_id: 1, product_id: 2}, %{meal_id: 2, product_id: 4}]

Just replace implementation

&1.meal_id * 2

# with

search_in_db(&1.meal_id)

BTW I doubt this is an efficient way to work with db, I would rather collect meal_ids, then perform one query to fetch product_ids (where … in …), or a query that fetch both.

6 Likes

This happens as the code recursively works through each element of the list. Consider this:

defmodule Fp do

  def shunt([], ys),
    do: ys
  def shunt([x|xs], ys),
    do: shunt(xs, [x|ys])

  def reverse(xs),
    do: shunt(xs,[])

  def foldl([], acc, _fun),
    do: acc
  def foldl([x|xs], acc, fun),
    do: foldl(xs, fun.(x,acc), fun) # this recursive call works through the list

  def map(xs, fun) do
    xs
    |> foldl([], cons_result_fn(fun))
    |> reverse()
  end

  defp cons_result_fn(fun),
    do: fn (x, acc) -> [fun.(x)|acc] end
#    do: &([fun.(&1)|&2])

end

defmodule Demo do
  import Fp, [only: [map: 2, foldl: 3]]

  def lookup_meal_product(meal_id) do
    case meal_id do
      1 ->
        {:ok, 450}
      2 ->
        {:ok, 330}
      _ ->
        {:error, "Not found"}
    end
  end

  defp merge_meal_product(%{meal_id: id} = org) do
    with {:ok, product_id} <- lookup_meal_product(id) do
      Map.put(org, :product_id, product_id)
    else
      _ -> # no product id
        org
    end
  end

  def product_map(meals),
    do: map(meals, &merge_meal_product/1)

  defp put_meal_product(%{meal_id: id} = org, acc) do
    with {:ok, product_id} <- lookup_meal_product(id) do
      [Map.put(org, :product_id, product_id) | acc]
    else
      _ -> # no product id - skip this meal
        acc
    end
  end

  def product_foldl(meals),
    do: foldl(meals, [], &put_meal_product/2)

end

meals = [%{meal_id: 1}, %{meal_id: 2}, %{meal_id: 9}]
meal_products = Demo.product_map(meals)
IO.inspect(meal_products)
meal_products = Demo.product_foldl(meals)
IO.inspect(meal_products)
$ elixir demo.exs
[%{meal_id: 1, product_id: 450}, %{meal_id: 2, product_id: 330}, %{meal_id: 9}]
[%{meal_id: 2, product_id: 330}, %{meal_id: 1, product_id: 450}]
$ 
2 Likes

Thanks!
@kokolegorille, i added only one thing(create new map - to “save” results) and works great:)

new = 
  mymeals 
  |> Enum.map(& Map.put(&1, :products, get_my_meals_product(&1.meal_id)) )

about query, yeah i know but i’m beginner in sql too:)
I have 2 tables:

My query:

  #Mymeals - my_meals table
  #Mymealsproducts - my_meals_products table
  query = from(mm in Mymeals,
    join: mmp in Mymealsproducts, on: mm.meal_id == mmp.meal_id,
    select: %{meal_id: mm.meal_id, meal_name: mm.meal_name, product_id: mmp.product_id},
    where: mm.user_id == ^user_id
  )

and i get:

[
  %{meal_id: 1, meal_name: "Example meal one", product_id: 1},
  %{meal_id: 1, meal_name: "Example meal one", product_id: 2},
  %{meal_id: 2, meal_name: "Example two!", product_id: 1},
]

but i need something like:

[
  %{meal_id: 1, meal_name: "Example meal one", [%{product_id: 1, product_id: 2}]},
  %{meal_id: 2, meal_name: "Example two!", [%{product_id: 1}]},
]
iex(1)> alias MusicDB.{Repo,Album,Track}
[MusicDB.Repo, MusicDB.Album, MusicDB.Track]
iex(2)> import Ecto.Query
Ecto.Query
iex(3)> prep_track = fn %Track{id: track_id} -> %{track_id: track_id} end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(4)> prep_tracks = fn tracks -> Enum.map(tracks, prep_track) end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(5)> prep_album = fn %Album{id: album_id, title: title, tracks: tracks} ->
...(5)>   %{album_id: album_id, title: title, tracks: prep_tracks.(tracks)}
...(5)> end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(6)> artist_id = 1
1
iex(7)> query = from(a in Album,
...(7)>   preload: :tracks,
...(7)>   where: a.artist_id == ^artist_id
...(7)> )
#Ecto.Query<from a in MusicDB.Album, where: a.artist_id == ^1,
 preload: [:tracks]>
iex(8)> query |> Repo.all() |> Enum.map(prep_album)

15:43:50.757 [debug] QUERY OK source="albums" db=2.7ms decode=2.2ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id" FROM "albums" AS a0 WHERE (a0."artist_id" = $1) [1]
 
15:43:50.765 [debug] QUERY OK source="tracks" db=2.4ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id", t0."album_id" FROM "tracks" AS t0 WHERE (t0."album_id" = ANY($1)) ORDER BY t0."album_id" [[1, 2]]
[
  %{
    album_id: 2,
    title: "Cookin' At The Plugged Nickel",
    tracks: [
      %{track_id: 6},
      %{track_id: 7},
      %{track_id: 8},
      %{track_id: 9},
      %{track_id: 10}
    ]
  },
  %{
    album_id: 1,
    title: "Kind Of Blue",
    tracks: [
      %{track_id: 1},
      %{track_id: 2},
      %{track_id: 3},
      %{track_id: 4},
      %{track_id: 5}
    ]
  }
]
iex(9)> 
1 Like