Count total price of order

Hi. Currently, I try to go trough all products and count the price of all of then and update order price. However, I struggle with this for some while, because I, in the end, receive different values in different loop stages.

The end result what I want to archive, is to have order.price with total price of all products.

  def add(variant_ids \\ [], order \\ %{}) do
    # If price is nil, then set inital value as 0.00
    case order.price do
      nil -> order = %{order | price: Decimal.new(0)}
      _ ->
    end

    for variant_id <- variant_ids do
      {_, combination} = variant_id

      variant = Variant
      |> Repo.get!(combination["variant_id"])
      |> Repo.preload([:color, :product, :product_size, :product_type, :photo])

      for _i <- 1..String.to_integer(combination["count"]) do
        order = %{order | price: Decimal.add(order.price, variant.product.price)}
      end
      IO.inspect order # Price is updated, however only for one iteration, the last interation result is lost
    end

    IO.inspect order # Price stays decimal 0
  end
3 Likes

That looks like exactly what it should be doing.

Also, why are you setting order to new bindings all over the place? It looks like you are trying to use it like a mutable variable, but there is no mutation here, should remove those. Also, why are you setting order inside a for when there is nothing done with that order, thus making that binding useless? o.O?

2 Likes

Well, the idea what I want to archive is to set total product price amount to order.price. How would you suggest to do this?

1 Like

I’d suggest you take a look at Enum.reduce

Enum.reduce([%{price: 123}, %{price: 456}], 0, fn(%{price: price}, acc) -> acc + price end)
3 Likes

And how I can get the final reduce value to order.price?

1 Like

Sometimes it’s easier to solve a problem if you write down small and simple steps

  1. Get a list of products (variants, etc)
  2. Calculate to total price (Enum.reduce)
  3. Update the order.price with the total price
2 Likes

I took the liberty to rewrite your code.

Note that it’s not tested! Aside from that I would suggest you read up on some Elixir books / tutorials, it seems your previous experience in other languages is what’s making the code look strange.

  1. The defaults in the add function make no sense, you will never call this action without params (hence invoking the defaults).
  2. Don’t preload relations that you are not going to use, color, photo and etc are not needed to do what you need, calculate the total order price.
  3. Take a closer look at @OvermindDL1 reply
defmodule OrderHandler do
  def add(variant_ids, order) when is_list(variant_ids) and is_map(order) do
    total_price =
      Enum.reduce(variant_ids, 0, fn({_, combination}, acc) ->
        variant =
          Variant
          |> Repo.get!(combination["variant_id"])
          |> Repo.preload([:product])

        acc + variant.product.price
      end)

    %{order | price: Decimal.new(total_price)}
  end
end

6 Likes

Thanks @hlx and @OvermindDL1
I guess I should dive deep in elixir more to move slightly easier from other languages.

1 Like

Sorry, I’m probably missing something here, but why not just query the database once doing a sum, instead of doing Repo.get! for each row?

3 Likes

Sure, currently I do that, this was just one try to do it in this way. And I did this only because I wanted to learn more how to do these things in Elixir.

1 Like

Oy! My last message was more terse than I wanted! I thought I rewrote it with the solutions, heh. ^.^

Remember that Elixir/Erlang is a functional language (impure). Haskell is a pure functional language (although the operator overloading is crazy there), but it would be a great language to learn how to program functionally properly (since you have no choice but to do it the proper way in Haskell) and it will help your code when you program in other more mainstream languages too, it is worth learning (with plenty of tutorials! Learn you a Haskell is a fun one). :slight_smile:

OCaml is definitely easier to read than Haskell but it is not quite ‘pure’ either (it is more like Elixir/Erlang), however if you ignore ref’s and Array’s in OCaml then it might be a better way to learn. :slight_smile:

It is not the language that is important though, but rather the patterns of programming, these functional patterns once learned will help you in any language you use. :slight_smile:

2 Likes

Why acc for me converts from 0 to [1] if I even do acc + 1 in Enum.reduce

1 Like