small medium large xlarge

Dave_gnome_head_isolated_pragsmall
16 Jul 2013, 02:25
Dave Thomas (387 posts)
  • Pragmatic Bookshelf has offices in Texas (TX) and North Carolina (NC), so we have to charge sales tax on orders shipped to these states. The rates can be expressed as a keyword list1

    tax_rates = [ NC: 0.075, TX: 0.08 ]
    

    Here’s a list of orders:

    orders = [
        [ id: 123, ship_to: :NC, net_amount: 100.00 ],
        [ id: 124, ship_to: :OK, net_amount:  35.50 ],
        [ id: 125, ship_to: :TX, net_amount:  24.00 ],
        [ id: 126, ship_to: :TX, net_amount:  44.80 ],
        [ id: 127, ship_to: :NC, net_amount:  25.00 ],
        [ id: 128, ship_to: :MA, net_amount:  10.00 ],
        [ id: 129, ship_to: :CA, net_amount: 102.00 ],
        [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]
    

    Write a function that takes both lists and returns a copy of the orders, but with an extra field, total_amount which is the net plus sales tax. If a shipment is not to NC or TX, there’s no tax applied.

A Possible Solution</summary>

defmodule Tax do

  def orders_with_total(orders, tax_rates) do
    orders |> Enum.map(add_total_to(&1, tax_rates))
  end

  def add_total_to(order = [id: _, ship_to: state, net_amount: net], tax_rates) do
    tax_rate = Keyword.get(tax_rates, state, 0)
    tax      = net*tax_rate
    total    = net+tax
    Keyword.put(order, :total_amount, total)
  end

end


tax_rates =  [ NC: 0.075, TX: 0.08 ]

orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount:  35.50 ],
  [ id: 125, ship_to: :TX, net_amount:  24.00 ],
  [ id: 126, ship_to: :TX, net_amount:  44.80 ],
  [ id: 127, ship_to: :NC, net_amount:  25.00 ],
  [ id: 128, ship_to: :MA, net_amount:  10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 120, ship_to: :NC, net_amount:  50.00 ] 
]

IO.inspect Tax.orders_with_total(orders, tax_rates)


</details>

  1. I wish it were that simple….

Felipe_avatar_pragsmall
10 Sep 2013, 15:59
Felipe Coury (2 posts)

My naïve implementation was:

defmodule Taxes do
  def process_orders([], _tax_rates), do: []
  def process_orders([ head | tail ], tax_rates) do
    state = head[:ship_to]
    net = head[:net_amount]

    tax = tax_rates[state]

    if tax do
      tax_percent = 1 + tax
      total = net * tax_percent
    else
      total = net
    end

    new_head = Dict.put head, :total_amount, total
    [ new_head | process_orders(tail, tax_rates) ]
  end
end

tax_rates = [ NC: 0.075, TX: 0.08 ]
orders = [
    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
    [ id: 124, ship_to: :OK, net_amount:  35.50 ],
    [ id: 125, ship_to: :TX, net_amount:  24.00 ],
    [ id: 126, ship_to: :TX, net_amount:  44.80 ],
    [ id: 127, ship_to: :NC, net_amount:  25.00 ],
    [ id: 128, ship_to: :MA, net_amount:  10.00 ],
    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
    [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]

IO.inspect Taxes.process_orders(orders, tax_rates)
Casual - sqr_pragsmall
25 Sep 2013, 02:35
Ryan Cromwell, Sr (4 posts)

My implementation using lots of little functions composed. This helped me learn the & operator. Some interesting discussion on the Elixir mailing list from this can be found here.

tax_rates = [ NC: 0.075, TX: 0.08 ]

orders = [ [ id: 123, ship_to: :NC, net_amount: 100.00 ],
            [ id: 124, ship_to: :OK, net_amount: 35.50 ],
            [ id: 125, ship_to: :TX, net_amount: 24.00 ],
            [ id: 126, ship_to: :TX, net_amount: 44.80 ],
            [ id: 127, ship_to: :NC, net_amount: 25.00 ],
            [ id: 128, ship_to: :MA, net_amount: 10.00 ],
            [ id: 129, ship_to: :CA, net_amount: 102.00 ],
            [ id: 120, ship_to: :NC, net_amount: 50.00 ] 
         ]

taxrateforstate = & Keyword.get tax_rates, &1, 0
taxratefororder = & (&1[:ship_to] |> taxrateforstate.())
taxfororder = & (&1[:net_amount] * taxratefororder.(&1))
totalfororder = & (&1[:net_amount] + &1[:tax])

orderwithtaxes = & Keyword.put &1, :tax, taxfororder.(&1)
orderwithtotal = & Keyword.put &1, :total, totalfororder.(&1)

orders |>
  Stream.map(orderwithtaxes.(&1)) |>
  Stream.map(orderwithtotal.(&1)) |>
  Enum.each( IO.inspect &1 )
Generic-user-small
24 Dec 2013, 16:12
Thomas Powell (1 post)

Looks like the code in the “possible solution”

    orders |> Enum.map(add_total_to(&1, tax_rates))

now needs to be written

    orders |> Enum.map(&(add_total_to(&1, tax_rates)))
Snapshot_2008101_248x248_pragsmall
24 Dec 2013, 20:37
Rafael Rosa (2 posts)

My implementation:

defmodule Shipping do
  def tax_rates, do: [ NC: 0.075, TX: 0.08 ]

  def orders, do: [
    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
    [ id: 124, ship_to: :OK, net_amount: 35.50 ],
    [ id: 125, ship_to: :TX, net_amount: 24.00 ],
    [ id: 126, ship_to: :TX, net_amount: 44.80 ],
    [ id: 127, ship_to: :NC, net_amount: 25.00 ],
    [ id: 128, ship_to: :MA, net_amount: 10.00 ],
    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
    [ id: 120, ship_to: :NC, net_amount: 50.00 ] ]

  def tax_for_state(state) do
    found = Enum.filter Shipping.tax_rates, fn {s, _} -> s == state end
    found[state] || 0.0
  end

  def calculate do
    lc [id: id, ship_to: ship_to, net_amount: net_amount] inlist orders do
      tax = tax_for_state(ship_to) * net_amount
      total = net_amount + tax
      [id: id, ship_to: ship_to, net_ammount: net_amount, total_amount: total]
    end
  end
end

Shipping.calculate

[[id: 123, ship_to: :NC, net_ammount: 100.0, total_amount: 107.5],
 [id: 124, ship_to: :OK, net_ammount: 35.5, total_amount: 35.5],
 [id: 125, ship_to: :TX, net_ammount: 24.0, total_amount: 25.92],
 [id: 126, ship_to: :TX, net_ammount: 44.8, total_amount: 48.384],
 [id: 127, ship_to: :NC, net_ammount: 25.0, total_amount: 26.875],
 [id: 128, ship_to: :MA, net_ammount: 10.0, total_amount: 10.0],
 [id: 129, ship_to: :CA, net_ammount: 102.0, total_amount: 102.0],
 [id: 120, ship_to: :NC, net_ammount: 50.0, total_amount: 53.75]]
Hugo-oculos-escuro_pragsmall
30 Dec 2013, 18:42
Hugo Baraúna (3 posts)

My solution using pattern matching and some clauses:

defmodule Tax do
  @tax_rates [ NC: 0.075, TX: 0.08 ]

  def orders_with_total(orders) do
    Enum.map(orders, &order_with_total/1)
  end

  def order_with_total(order = [_, { :ship_to, :NC }, _]) do
    order ++ [total_amount: order[:net_amount] + order[:net_amount] * @tax_rates[:NC]]
  end

  def order_with_total(order = [_, { :ship_to, :TX }, _]) do
    order ++ [total_amount: order[:net_amount] + order[:net_amount] * @tax_rates[:TX]]
  end

  def order_with_total(order) do
    order ++ [total_amount: order[:net_amount]]
  end
end

Generic-user-small
17 Jan 2014, 16:19
Brian Ehmann (1 post)

I could not figure out how to eliminate the if statement without having to hard code the rates.

defmodule Taxes do
  def update(_, []), do: []
	
  def update(rates, [head | tail]) do
    if rates[head[:ship_to]] do
      [ Dict.put(head, :total_amount, head[:net_amount] * (1 + rates[head[:ship_to]])) | update(rates, tail) ]
    else
      [ Dict.put(head, :total_amount, head[:net_amount]) | update(rates, tail) ]
    end
  end
end
Generic-user-small
11 Mar 2014, 08:00
Lorenzo lopez (1 post)

A little refining on on Hugo solution


defmodule Tax do

  @tax_rates [ NC: 0.075, TX: 0.08 ]

  def totals(orders) do
    Enum.map orders, &_total/1
  end

  defp _total(order_line = [ _, { :ship_to, place }, _]) when place in [:NC, :TX] do
    order_line ++ [total_amount: order_line[:net_amount]*(1+@tax_rates[place])]
  end

  defp _total(order_line), do: order_line ++ [total_amount: order_line[:net_amount]]

end

Patrick_pragsmall
04 Aug 2014, 07:54
Patrick Oscity (13 posts)

Here’s my solution:

defmodule Accounting do
  def apply_tax(orders, tax_rates) do
    for order <- orders do
      Dict.put order, :total_amount, total_amount(order, tax_rates)
    end
  end

  def total_amount(order, tax_rates) do
    order[:net_amount] * (1.0 + tax_rate(order[:ship_to], tax_rates))
  end

  def tax_rate(state, tax_rates) do
    tax_rates[state] || 0.0
  end
end

tax_rates = [NC: 0.075, TX: 0.08]
orders = [
  [id: 123, ship_to: :NC, net_amount: 100.00],
  [id: 124, ship_to: :OK, net_amount:  35.50],
  [id: 125, ship_to: :TX, net_amount:  24.00],
  [id: 126, ship_to: :TX, net_amount:  44.80],
  [id: 127, ship_to: :NC, net_amount:  25.00],
  [id: 128, ship_to: :MA, net_amount:  10.00],
  [id: 129, ship_to: :CA, net_amount: 102.00],
  [id: 120, ship_to: :NC, net_amount:  50.00]
]

IO.inspect Accounting.apply_tax(orders, tax_rates)
Blacksad-cov_sq_pragsmall
18 Sep 2014, 15:17
Porco Wong (3 posts)

This was my implementation:

defmodule Tax do

  def process_order(orders, rates) do
    for order <- orders do
      rate = Keyword.get rates, order[:ship_to], 0
      Keyword.put order, :total_account, add_total_account(order[:net_amount], rate)
    end
  end

  defp add_total_account(amount, rate), do: amount * ( 1 + rate )

end

tax_rates = [NC: 0.075, TX: 0.08]
orders = [
  [id: 123, ship_to: :NC, net_amount: 100.00],
  [id: 124, ship_to: :OK, net_amount:  35.50],
  [id: 125, ship_to: :TX, net_amount:  24.00],
  [id: 126, ship_to: :TX, net_amount:  44.80],
  [id: 127, ship_to: :NC, net_amount:  25.00],
  [id: 128, ship_to: :MA, net_amount:  10.00],
  [id: 129, ship_to: :CA, net_amount: 102.00],
  [id: 120, ship_to: :NC, net_amount:  50.00]
]

IO.inspect Tax.process_order orders, tax_rates

Nathan-6kites_pragsmall
25 Sep 2014, 17:14
Nathan Feaver (9 posts)

I round the total so that the output makes more sense. I also extracted the round operation to its own method so that other potential calculations would be consistent.

defmodule Order do
  def calculate_totals(orders, tax_rates) do
    for order  <- orders,
    do: Dict.put(order, :total_amount, _total(order))
  end

  defp _tax_rate(tax_rates, state), do: tax_rates[state] || 0

  defp _total(order, tax_rates), do: _total(order[:net_amount], tax_rates, order[:ship_to])
  defp _total(net, tax_rates, state) do
    net * (1 + _tax_rate(tax_rates, state))
      |> _round(2)
  end

  defp _round(dollar_amount), do: Float.round(dollar_amount, 2)
end

tax_rates = [ NC: 0.075, TX: 0.08 ]
orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount: 35.50 ],
  [ id: 125, ship_to: :TX, net_amount: 24.00 ],
  [ id: 126, ship_to: :TX, net_amount: 44.80 ],
  [ id: 127, ship_to: :NC, net_amount: 25.00 ],
  [ id: 128, ship_to: :MA, net_amount: 10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 120, ship_to: :NC, net_amount: 50.00 ] ]

IO.inspect(Order.calculate_totals(orders, tax_rates))
Generic-user-small
07 Nov 2014, 19:51
Matt Bridges (1 post)

My implementation. Some pattern matching and comprehensions.

defmodule TaxRates do
  def tax_rates, do: [ NC: 0.075, TX: 0.08 ]
  def orders do
    [
      [ id: 123, ship_to: :NC, net_amount: 100.00 ],
      [ id: 124, ship_to: :OK, net_amount:  35.50 ],
      [ id: 125, ship_to: :TX, net_amount:  24.00 ],
      [ id: 126, ship_to: :TX, net_amount:  44.80 ],
      [ id: 127, ship_to: :NC, net_amount:  25.00 ],
      [ id: 128, ship_to: :MA, net_amount:  10.00 ],
      [ id: 129, ship_to: :CA, net_amount: 102.00 ],
      [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]
  end

  def calculate_totals, do: for order <- orders, do: add_total_to_order(order)

  defp add_total_to_order(order = [id: _, ship_to: location, net_amount: net]) do
    order ++ [total_amount: total_with_tax(location, net)]
  end

  defp total_with_tax(location, net), do: net * calculate_tax_rate(location)
  defp calculate_tax_rate(location),  do: 1 + Keyword.get(tax_rates, location, 0)
end
Generic-user-small
12 Nov 2014, 00:10
Elliot Finley (13 posts)

My solution. I like Dave’s better - getting values via pattern matching in parameters.

defmodule Tax do
	def tax(tax_rates, orders) do
		Enum.map(orders, &(_calculate_tax(&1,tax_rates)))
	end

	defp _calculate_tax(order, tax_rates) do
		ship_to = Keyword.get(order, :ship_to)
		net_amount = Keyword.get(order, :net_amount)
		tax_rate = Keyword.get(tax_rates, ship_to)
		multiplier = _tax_multiplier(tax_rate)
		total_amount = net_amount * multiplier
		Keyword.put(order, :total_amount, total_amount)
	end

	defp _tax_multiplier(nil) do 1 end
	defp _tax_multiplier(rate) do 1 + rate end
end
Generic-user-small
15 Dec 2014, 21:38
Jose C Garcia-Sacristan (1 post)

Here is mine. Quite neat, actually. :-)

defmodule Orders
do
  def calculate_tax(orders, tax_rates) do
    for [id: id, ship_to: ship_to, net_amount: net_amount] <- orders, tax = Dict.get(tax_rates, ship_to, 0), do: [id: id, ship_to: ship_to, net_amount: net_amount, total_amount: net_amount + (net_amount * tax)]
  end
end

Generic-user-small
28 Dec 2014, 17:02
Pierre Sugar (57 posts)
defmodule Orders do

  def tax_rates do
    [ NC: 0.075, TX: 0.08 ]
  end

  def orders do
    [
      [ id: 123, ship_to: :NC, net_amount: 100.00 ],
      [ id: 124, ship_to: :OK, net_amount:  35.50 ],
      [ id: 125, ship_to: :TX, net_amount:  24.00 ],
      [ id: 126, ship_to: :TX, net_amount:  44.80 ],
      [ id: 127, ship_to: :NC, net_amount:  25.00 ],
      [ id: 128, ship_to: :MA, net_amount:  10.00 ],
      [ id: 129, ship_to: :CA, net_amount: 102.00 ],
      [ id: 120, ship_to: :NC, net_amount:  50.00 ]
    ]
  end

  def totals(tax_rates, orders) do
    for x <- orders do 
      Enum.into([total: total(x[:net_amount], tax_rates[x[:ship_to]]) ], x)
    end
  end

  defp total(value, tax), do: (if tax, do: ((1+tax) * value), else: value)
end

IO.puts inspect Orders.totals(Orders.tax_rates, Orders.orders)
Matteo_250_pragsmall
22 Apr 2015, 21:42
Matteo Giachino (2 posts)

This is my take, using only a list comprehension

def orders_with_shipment(tax_rates, orders) do
  for o <- orders, Enum.member?([:NC, :TX], o[:ship_to]),
      do: o ++ [total_amount: o[:net_amount] + (o[:net_amount] * tax_rates[o[:ship_to]])]
end
Generic-user-small
05 May 2015, 09:09
Derick Thomas (3 posts)

This is what I did:


defmodule SalesTax do
  def get_taxed(orders, tax_rates) do
    for order <- orders, tax_rate = tax_rates[order[:ship_to]] || 0.0, do: order ++ [total_amount: (1.0+tax_rate) * order[:net_amount]]  end
end

Result


iex(63)> SalesTax.get_taxed(orders, tax_rates)
[[id: 123, ship_to: :NC, net_amount: 100.0, total_amount: 107.5],
 [id: 124, ship_to: :OK, net_amount: 35.5, total_amount: 35.5],
 [id: 125, ship_to: :TX, net_amount: 24.0, total_amount: 25.92],
 [id: 126, ship_to: :TX, net_amount: 44.8, total_amount: 48.384],
 [id: 127, ship_to: :NC, net_amount: 25.0, total_amount: 26.875],
 [id: 128, ship_to: :MA, net_amount: 10.0, total_amount: 10.0],
 [id: 129, ship_to: :CA, net_amount: 102.0, total_amount: 102.0],
 [id: 120, ship_to: :NC, net_amount: 50.0, total_amount: 53.75]]
iex(64)> 

4470516_pragsmall
15 May 2015, 06:44
Cifer Y (3 posts)
defmodule Tax do
  def add_total(orders, tax_rate) do
    [NC: nc_rate, TX: tx_rate] = tax_rate
    for order <- orders do
      [id: _, ship_to: city, net_amount: net] = order
      case city do
        :NC -> Keyword.put(order, :total_amount, net + net * nc_rate)
        :TX -> Keyword.put(order, :total_amount, net + net * tx_rate)
          _ -> nil
      end
    end
  end
end


orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount:  35.50 ],
  [ id: 125, ship_to: :TX, net_amount:  24.00 ],
  [ id: 126, ship_to: :TX, net_amount:  44.80 ],
  [ id: 127, ship_to: :NC, net_amount:  25.00 ],
  [ id: 128, ship_to: :MA, net_amount:  10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 120, ship_to: :NC, net_amount:  50.00 ]
]

tax_rates = [NC: 0.075, TX: 0.08]

Tax.add_total(orders, tax_rates) |> inspect |> IO.puts

Generic-user-small
05 Jun 2015, 22:54
David Phillips (1 post)

Mine’s a little screwy, but I like it

#!/usr/bin/env elixir

defmodule TaxAndOrders do
  def total(orders,tax) do
    Enum.map(orders, 
      fn [a,b={:ship_to, state},c={:net_amount, n}] -> # Deconstruct list and pull out the state and order amount
        # Pull out the states in the tax list and check if current state is in there
        if state in (for {st, _rate} <- tax do st end) do 
          [a,b,c, total_amount: n+(n*tax[state])] # If it is, apply the appropriate tax rate
        else
          [a,b,c, total_amount: n] # If not just have the total_amount equal the net_amount
        end
      end)       
  end
end


tax_rates = [ NC: 0.075, TX: 0.08 ]
orders = [
    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
    [ id: 124, ship_to: :OK, net_amount:  35.50 ],
    [ id: 125, ship_to: :TX, net_amount:  24.00 ],
    [ id: 126, ship_to: :TX, net_amount:  44.80 ],
    [ id: 127, ship_to: :NC, net_amount:  25.00 ],
    [ id: 128, ship_to: :MA, net_amount:  10.00 ],
    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
    [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]

orders |> TaxAndOrders.total(tax_rates) |> IO.inspect
2011-11-23-square_pragsmall
10 Jun 2015, 10:30
Daniel Garcia (4 posts)

My solution

defmodule Tax do
  def add_total_to(orders, tax_rates) do
    Enum.map(orders, &(&1 ++ [total_amount: total_amount(&1, tax_rates)]))
  end

  defp total_amount(order, tax_rates) do
    tax_rate = Keyword.get(tax_rates, order[:ship_to], 0)

    order[:net_amount] * (1 + tax_rate)
  end
end
Generic-user-small
22 Jul 2015, 06:20
Dave (3 posts)

My solution (a bit clunky)

  def calc_total(tax_rates, orders) do
    tax_states = Dict.keys(tax_rates)
    for x <- orders do
      ship_to_val = Dict.fetch!(x, :ship_to)
      net_amount_val = Dict.fetch!(x, :net_amount)
      case Enum.member?(tax_states, ship_to_val) do
        true -> tax_rate = Dict.fetch!(tax_rates, ship_to_val)
        false -> tax_rate = 0
      end
      tax_amount =  tax_rate * net_amount_val
      Dict.put_new(x, :total_amount, tax_amount + net_amount_val)
    end
  end
4c5c2c297ed9f4664cfbe7733a011fb2_pragsmall
14 Aug 2015, 15:43
Artem Medeusheyev (8 posts)
defmodule Taxes do
  def add_total(taxes, orders) do
    Enum.map(orders, fn(order = [id: _, ship_to: to, net_amount: amnt]) ->
      Keyword.put(order, :total_amount, amnt + amnt * Keyword.get(taxes, to, 1))
    end)
  end
end
11010008_886028121441016_8781768823206677829_n_pragsmall
19 Aug 2015, 21:01
Tiago Davi (2 posts)

Dave, Why Does your function “add_total_to” work without “&” in front ?

My function _add_tax only works with “&” in front or show:

** (CompileError) tax.exs:19: unhandled &1 outside of a capture

This is my implementation

defmodule Tax do 
	def get_tax_rates do 
		[ NC: 0.075, TX: 0.08 ]
	end
	def get_orders do
		[
		    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
		    [ id: 124, ship_to: :OK, net_amount: 35.50 ],
		    [ id: 125, ship_to: :TX, net_amount: 24.00 ],
		    [ id: 126, ship_to: :TX, net_amount: 44.80 ],
		    [ id: 127, ship_to: :NC, net_amount: 25.00 ],
		    [ id: 128, ship_to: :MA, net_amount: 10.00 ],
		    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
		    [ id: 120, ship_to: :NC, net_amount: 50.00 ] 
		]
	end
	def calculate(orders, tax_rates), do: _calculate(orders, tax_rates)
	defp _calculate(orders, tax_rates) do 
		Enum.map(orders, &_add_tax(&1, tax_rates))		
	end
	defp _add_tax(order = [id: id, ship_to: ship_to, net_amount: net_amount], tax_rates) do 
		tax_rate = Keyword.get(tax_rates, ship_to, 0)
		total = (net_amount * tax_rate) + net_amount		
		Keyword.put(order, :total_amount, total)
	end
end
Generic-user-small
15 Nov 2015, 05:11
Katsuma Ito (1 post)
defmodule Price do
  def total(orders, tax) do
    for order <- orders, into: [], do: order ++ [
      total_amount: order[:net_amount] * (1 + (tax[order[:ship_to]] || 0))
    ]
  end
end

tax_rates = [ NC: 0.075, TX: 0.08 ]
calc_total_price = &Price.total(&1, tax_rates)

orders = [
    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
    [ id: 124, ship_to: :OK, net_amount:  35.50 ],
    [ id: 125, ship_to: :TX, net_amount:  24.00 ],
    [ id: 126, ship_to: :TX, net_amount:  44.80 ],
    [ id: 127, ship_to: :NC, net_amount:  25.00 ],
    [ id: 128, ship_to: :MA, net_amount:  10.00 ],
    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
    [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]

orders |> calc_total_price.() |> IO.inspect

(It’s little depended on the data type.)

Generic-user-small
08 Dec 2015, 06:37
Charles Okwuagwu (1 post)
def report do
    tax_rates = [ NC: 0.075, TX: 0.08 ]
    orders = [
        [ id: 123, ship_to: :NC, net_amount: 100.00 ],
        [ id: 124, ship_to: :OK, net_amount: 35.50 ],
        [ id: 125, ship_to: :TX, net_amount: 24.00 ],
        [ id: 126, ship_to: :TX, net_amount: 44.80 ],
        [ id: 127, ship_to: :NC, net_amount: 25.00 ],
        [ id: 128, ship_to: :MA, net_amount: 10.00 ],
        [ id: 129, ship_to: :CA, net_amount: 102.00 ],
        [ id: 120, ship_to: :NC, net_amount: 50.00 ] ]
    
    get_tax = 
        fn(city)-> 
            case tax_rates[city] do
                nil -> 0
                tax -> tax
            end
        end
        
    for o <- orders, 
        do: [id: o[:id], ship_to: o[:ship_to], net_amount: o[:net_amount], total_amount: o[:net_amount] * (1 + get_tax.(o[:ship_to]))] 
end
976310_10201335063559319_1050362366_o_pragsmall
26 Jan 2016, 02:57
Andrés Alejandro García Hurtado (4 posts)

My Two Possible Solutions

defmodule Tax do
  def orders_with_total(orders, taxes) do
    orders 
      |> Enum.map(fn order = [id: _, ship_to: ship_to, net_amount: net_amount] -> 
          tax_rate = if Keyword.has_key?(taxes, ship_to), do: taxes[ship_to], else: 0
          total_amount =  net_amount + (net_amount * tax_rate)
          Keyword.put(order, :total_amount, total_amount)
      end )
      |> Enum.sort(& &1[:id] >= &2[:id])
  end

  def orders_with_total_2(orders, tax_rates) do
    (for order = [id: _, ship_to: ship_to, net_amount: net_amount] <- orders,
        {state, tax_rate} <- tax_rates,
        do: (
          total_amount = if ship_to == state, do: net_amount + (net_amount * tax_rate), else: net_amount
          Keyword.put(order, :total_amount, total_amount)
        )
    )
      |> Enum.sort(& &1[:total_amount] >= &2[:total_amount])
      |> Enum.uniq_by(& &1[:id])
      |> Enum.sort(& &1[:id] >= &2[:id])
  end
end

Tax.orders_with_total(orders, tax_rates) == Tax.orders_with_total_2(orders, tax_rates)
169065098_pragsmall
25 Dec 2015, 14:02
Bartosz Magryś (4 posts)

Here is mine solution (no guards and validation):

defmodule Awesome do
  def total(tax_rates, orders) do
    for order <- orders do
      if rate = tax_rates[order[:ship_to]] do
        total = order[:net_amount] + (rate * order[:net_amount])
      else
        total = order[:net_amount]
      end
      [ order | [ total_amount: total ] ]
    end
  end
end
Prof_pragsmall
27 Dec 2015, 07:03
David Qorashi (8 posts)

Here is my implementation:

defmodule Tax do
  def orders_with_total(orders) do
    tax_rates = [ NC: 0.075, TX: 0.08 ]
    for [id, ship_to = {_, state}, net_amount = {_, value}] <- orders do
      [id, ship_to, net_amount, {:total_amount, (Dict.get(tax_rates, state ) || 1) * value + value }]
    end
  end
end

orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount:  35.50 ],
  [ id: 125, ship_to: :TX, net_amount:  24.00 ],
  [ id: 126, ship_to: :TX, net_amount:  44.80 ],
  [ id: 127, ship_to: :NC, net_amount:  25.00 ],
  [ id: 128, ship_to: :MA, net_amount:  10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]

Tax.orders_with_total(orders) |> IO.inspect
Generic-user-small
12 Jan 2016, 04:23
Uri Gorelik (1 post)

Using only a comprehension, no stdlib functions and no user defined functions.

tax_rates = [ NC: 0.075, TX: 0.08 ]

orders = [
    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
    [ id: 124, ship_to: :OK, net_amount:  35.50 ],
    [ id: 125, ship_to: :TX, net_amount:  24.00 ],
    [ id: 126, ship_to: :TX, net_amount:  44.80 ],
    [ id: 127, ship_to: :NC, net_amount:  25.00 ],
    [ id: 128, ship_to: :MA, net_amount:  10.00 ],
    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
    [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]

modified_order =
  for order = [ id: _, ship_to: o_state, net_amount: net_amount ] <- orders,
    { t_state, tax_rate } <- tax_rates,
    o_state == t_state do
    [ total_amount: net_amount * (1 + tax_rate ) ] ++ order
  end

IO.inspect modified_order
Generic-user-small
31 Jan 2016, 14:20
Antony Sastre (1 post)

I tried to keep it small using comprehensions, suggestions?

defmodule Tax do
  def parse_orders(orders, taxes) do
    for order <- orders do
      [id: _, ship_to: state, net_amount: net_amount] = order
      total_amount = net_amount * (1 + Keyword.get(taxes, state, 0))
      order ++ [total_amount: total_amount]
    end
  end
end
Generic-user-small
03 Feb 2016, 02:36
Nico piderman (4 posts)
defmodule Orders do

  def tax_rate(:NC), do: 0.075
  def tax_rate(:TX), do: 0.08
  def tax_rate(_), do: 0

  def calculate_totals(orders) do
    for [id, {:ship_to, state}, {:net_amount, net}] <- orders, 
      rate = tax_rate(state),
      multiplier = 1 + rate,                                                                                                                                
      total = net * multiplier,
      do: [id, {:ship_to, state}, {:net_amount, net}, {:total_amount, Float.round(total, 2)}]
  end 

end
Generic-user-small
14 Feb 2016, 04:58
Gerry Shaw (3 posts)

@Antony Sastre I did it almost exactly like yours. https://github.com/gshaw/elixir/blob/master/recursion/taxes.exs

Generic-user-small
21 Feb 2016, 04:52
Mark Wilbur (3 posts)

I like using patten matching for something like this. The solution is very manageable:

for x = [_, {_,loc}, {_,net}] <- orders, do:
  [x ++ [total_amount: net * (1 + (tax_rates[loc] || 0)) ]]

The only part I’m not happy with is the

[total_amount: net * (1 + (tax_rates[loc] || 0)) ]]

It seems like there should be a cleaner way to handle the nil than tacking on a || 0 to coerce it into a number.

Generic-user-small
03 Mar 2016, 12:33
Jelte Liekens (1 post)

My solution:

defmodule Taxes do

  def apply_taxes(orders, tax_rates) do
    for order <- orders, tax_rate <- tax_rates, do: apply_tax(order, tax_rate)
  end

  def apply_tax([_id, _ship_to, { :net_amount, amount }] = order, []) do
    order ++ [total_amount: amount]
  end

  def apply_tax([_id, { :ship_to, loc }, { :net_amount, amount }] = order, [{ loc, tax } | _tail]) do
    order ++ [total_amount: amount * (1 + tax)]
  end

  def apply_tax(order, [ _head | tail]) do
    apply_tax(order, tail)
  end

end

tax_rates = [ NC: 0.075, TX: 0.08 ]
orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount:  35.50 ],
  [ id: 125, ship_to: :TX, net_amount:  24.00 ],
  [ id: 126, ship_to: :TX, net_amount:  44.80 ],
  [ id: 127, ship_to: :NC, net_amount:  25.00 ],
  [ id: 128, ship_to: :MA, net_amount:  10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 130, ship_to: :NC, net_amount:  50.00 ] ]

IO.inspect Taxes.apply_taxes(orders, tax_rates)
Generic-user-small
06 Mar 2016, 00:18
Shane Burnham (2 posts)

I love how succinct some of these answers are. Mine is comparatively messy:


defmodule Tax do
  def salesTax(tax_rates, orders) do
    txAndNc = for [{id, no}, {ship_to, aCity}, {net_amount, net}] <- orders, {city, rate} <- tax_rates, aCity == city do
      [{id, no}, {ship_to, aCity}, {net_amount, net}, {:total_amount, net * rate + net}]
    end
    otherCities = for [{id, no}, {ship_to, aCity}, {net_amount, net}] <- orders, aCity != :NC, aCity != :TX do
      [{id, no}, {ship_to, aCity}, {net_amount, net}, {:total_amount, net}]
    end
    totals = txAndNc ++ otherCities
  end
end

Generic-user-small
09 Mar 2016, 23:42
Eric Litwin (2 posts)

@Mike Wilbur I like your solution the best. The only change is to remove the outer brackets in the do: block

Otherwise, it returns [[[id: 123, …],]] instead of [[id: 123, …],]

for x = [_, {_,loc}, {_,net}] <- orders, do:
  x ++ [total_amount: net * (1 + (tax_rates[loc] || 0)) ]

One way to remove the || 0 clause you don’t like is as follows:

  x ++ [total_amount: net * (1 + Keyword.get(tax_rates, loc, 0)) ]

I think your solution is more clear, at least coming from the Ruby world.

Generic-user-small
01 May 2016, 23:18
Paul Hollyer (11 posts)

Hi,

I’m a ‘rubyist’ trying to get a grounding in Elixir over the bank holiday weekend so I can get started with Phoenix. Must say, I’m really enjoying the book.

Here’s my solution:


  def calculate_totals do
    tax_rates = [ NC: 0.075, TX: 0.08]
    orders = [
      [id: 123, ship_to: :NC, net_amount: 100.00],
      [id: 124, ship_to: :OK, net_amount:  35.50],
      [id: 125, ship_to: :TX, net_amount:  24.00],
      [id: 126, ship_to: :TX, net_amount:  44.80],
      [id: 127, ship_to: :NC, net_amount:  25.00],
      [id: 128, ship_to: :MA, net_amount:  10.00],
      [id: 129, ship_to: :CA, net_amount: 102.00],
      [id: 130, ship_to: :NC, net_amount:  50.00],
    ]

     for order <- orders, result <- [_update_order_with_total(order,Keyword.fetch(tax_rates,order[:ship_to]))], do: result
  end

  def _update_order_with_total(order,{:ok, rate}), do: Enum.into([total_amount: order[:net_amount] + (order[:net_amount] * rate)],order)
  def _update_order_with_total(order,:error),      do: Enum.into([total_amount: order[:net_amount]],order)
Generic-user-small
13 May 2016, 02:36
Eric Lehner (5 posts)
tax_rates = [ NC: 0.075, TX: 0.08 ]

orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount: 35.50 ],
  [ id: 125, ship_to: :TX, net_amount: 24.00 ],
  [ id: 126, ship_to: :TX, net_amount: 44.80 ],
  [ id: 127, ship_to: :NC, net_amount: 25.00 ],
  [ id: 128, ship_to: :MA, net_amount: 10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 130, ship_to: :NC, net_amount: 50.00 ] ]

for order <- orders,
  ship_to = Keyword.get(order, :ship_to),
  net_amount = Keyword.get(order, :net_amount),
  tax_mult = Keyword.get(tax_rates, ship_to, 0) + 1,
  do: order ++ [{:total_amount, net_amount*tax_mult}]
Generic-user-small
13 May 2016, 15:36
Wioleta Jemieljańczuk (2 posts)
defmodule Tax do

  def calculate_tax(orders, tax_rates) do
    for n <- orders do
      n ++ [total_amount: n[:net_amount] * (1 + (tax_rates[n[:ship_to]] || 0.0) )]
    end
  end

end
Generic-user-small
09 Jun 2016, 03:57
Ivan Votti (3 posts)
defmodule Tax do
  def add_totals(orders, tax_rates) do
    for order = [_, ship_to: state, net_amount: amount] <- orders do
      tax = amount * (tax_rates[state] || 0)
      order ++ [total_amount: amount + tax]
    end
  end
end
Profilepicture_pragsmall
29 Jun 2016, 22:36
Romario López (5 posts)

I solved differently, Is my solution optimal?


defmodule MyList do
  def get_tax([], _tax_rates), do: []
  def get_tax([head = [_, _, _] | tail ], tax_rates) do
    [head ++ [total_amount: head[:net_amount] + (head[:net_amount] * Keyword.get(tax_rates, head[:ship_to], 0))] | get_tax(tail, tax_rates)]
  end
end

tax_rates = [NC: 0.075, TX: 0.08]
orders = [
  [ id: 123, ship_to: :NC, net_amount: 100.00 ],
  [ id: 124, ship_to: :OK, net_amount: 35.50 ],
  [ id: 125, ship_to: :TX, net_amount: 24.00 ],
  [ id: 126, ship_to: :TX, net_amount: 44.80 ],
  [ id: 127, ship_to: :NC, net_amount: 25.00 ],
  [ id: 128, ship_to: :MA, net_amount: 10.00 ],
  [ id: 129, ship_to: :CA, net_amount: 102.00 ],
  [ id: 130, ship_to: :NC, net_amount: 50.00 ]
]

IO.inspect MyList.get_tax(orders, tax_rates)

Generic-user-small
20 Jul 2016, 19:44
Karlo Smid (11 posts)

Here is my solution, not the best one:

defmodule TaxRate do
  def test_orders do
   [[id: 123, ship_to: :NC, net_amount: 100.0],
   [id: 124, ship_to: :OK, net_amount: 35.5],
   [id: 125, ship_to: :TX, net_amount: 24.0],
   [id: 126, ship_to: :TX, net_amount: 44.8],
   [id: 127, ship_to: :NC, net_amount: 25.0],
   [id: 128, ship_to: :MA, net_amount: 10.0],
   [id: 129, ship_to: :CA, net_amount: 102.0],
   [id: 130, ship_to: :CA, net_amount: 50.0]]
  end
  def test_rates do
    [NC: 0.075, TX: 0.08]
  end
  def calculate(rate,orders), do: calculate_have(rate,orders) ++ calculate_have_not(rate,orders)
  def calculate_have_not(rate,orders), do: for y <- orders, List.keyfind(rate,y[:ship_to],0) == nil, do: Keyword.put(y, :total_amount, y[:net_amount])
  def calculate_have(rate,orders), do: for x <- rate, y <- orders, elem(x,0) == y[:ship_to], do: Keyword.put(y, :total_amount, y[:net_amount] * (1 + elem(x,1)))
end
Generic-user-small
05 Sep 2016, 01:34
Chintan Thakkar (7 posts)
tax_rates = [ NC: 0.075, TX: 0.08 ]

orders = [
    [ id: 123, ship_to: :NC, net_amount: 100.00 ],
    [ id: 124, ship_to: :OK, net_amount:  35.50 ],
    [ id: 125, ship_to: :TX, net_amount:  24.00 ],
    [ id: 126, ship_to: :TX, net_amount:  44.80 ],
    [ id: 127, ship_to: :NC, net_amount:  25.00 ],
    [ id: 128, ship_to: :MA, net_amount:  10.00 ],
    [ id: 129, ship_to: :CA, net_amount: 102.00 ],
    [ id: 120, ship_to: :NC, net_amount:  50.00 ] ]

for order <- orders do
  taxrate = Keyword.get(tax_rates, Keyword.fetch!(order, :ship_to),0)
  tax_amount = order[:net_amount] * taxrate
  Enum.into order, [total_amount: order[:net_amount] + tax_amount]
end
Generic-user-small
21 Oct 2016, 12:38
Fabian Pfaff (2 posts)

The problem I see with solutions like @Mike Wilburs that rely on Pattern matching is that they rely on the ordering and size of the keywordlist. Entries that don’t match just get silently dropped.

You also probably want to get in the habit of prepending (which Keyword#put does) instead of appending to a list whenever possible, because of the associated performance cost.

To me it feels much more natural to implement this with Enum#map as we map the list of orders to a list of orders with an additional entry:

  def apply_taxes(tax_rates, orders) do
    Enum.map(orders, fn order ->
      tax_rate = Keyword.get(tax_rates, Keyword.fetch!(order, :ship_to), 0)
      total_amount = order[:net_amount] * (1 + tax_rate)
      Keyword.put(order, :total_amount, total_amount)
    end)
  end

If we actually should use Keyword.fetch! (missing :ship_to entry results in a KeyError) to get the shipping location depends on the specific application but is an interesting question to consider.

You must be logged in to comment