Adding to enumerables

Hey everyone,

I am trying to retrieve all work_items from the database and then add a calculated value to each of the work_items. I am stuck with this (that is not working):

  def index(conn, _params) do
    work_items = Tracking.list_work_items()

    work_items =
      work_items
        for work_item <- work_items do
          value = work_item_value(work_item)

          work_item =
            work_item
            |> Map.put(:value, value)
        end

    render(conn, "index.html", work_items: work_items)
  end

  def work_item_value(work_item) do
    work_item.duration_in_minutes/60 * work_item.hourly_rate_in_cents
  end

Any elaboration would be appreciated!

Hi, you can use the select_merge/3 function as it is explained here:
https://hexdocs.pm/ecto/Ecto.Query.html#select_merge/3

So the function list_work_items() would be like:

WorkItem
|> select_merge([work_item], %{value: work_item_value(work_item)})
|> Repo.all()
5 Likes

You’re doing too much rebinding of vars.

    work_items =
        for work_item <- work_items do
          value = work_item_value(work_item)
          Map.put(work_item, :value, value)
        end

    render(conn, "index.html", work_items: work_items)

Probably not the most efficient way to do it. This should be a query. I like @Laetitia solution.

2 Likes

It’s true You are using a lot of intermediate variables that are not required…

You could wrote it like this too, if value is a virtual attribute, or if You hadn’t a better solution with select_merge :slight_smile:

work_items = Enum.map(Tracking.list_work_items(), & %{&1 | value: work_item_value(&1)})

Thanks Laetitia! It appears that the code is not correct though. Even directly copying it I get 2 errors:

1:

** (Ecto.Query.CompileError) `work_item_value(work_item)` is not a valid query expression.

* If you intended to call a database function, please check the documentation
  for Ecto.Query to see the supported database expressions

* If you intended to call an Elixir function or introduce a value,
  you need to explicitly interpolate it with ^

2: When I fixed the first one (adding a ^ infront of the function):

warning: variable "work_item" does not exist and is being expanded to "work_item()", please use parentheses to remove the ambiguity or change the variable name
  lib/fourty/tracking.ex:22: Fourty.Tracking.list_work_items/0

I read the documentation, but my programming skills are not developed enough yet. Any more help?

Make sure you import Ecto.Query in a module before writing an Ecto.Query!

3 Likes

I have that! :slight_smile:

Oh yes! the function is not a valid query.
So you can use a fragment https://hexdocs.pm/ecto/Ecto.Query.html#module-fragments to feed the value in the select_merge. (btw, as kokolegorille said, it has to be a virtual value)

Please try this

WorkItem
|> select_merge([work_item], %{value: fragment("duration_in_minutes/60 * hourly_rate_in_cents")})
|> Repo.all()
5 Likes

Thanks for taking the time Laetitia! Now I got it. Have to read up on fragments.