small medium large xlarge

• 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
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….

My naïve implementation was:

``````defmodule Taxes do
def process_orders([], _tax_rates), do: []
def process_orders([ head | tail ], tax_rates) do

tax = tax_rates[state]

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

[ 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)
``````

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 )
``````

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)))
``````

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]]
``````

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

```

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
else
end
end
end
``````

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
``````

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)
``````

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
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

```

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))
```

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
```

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
``````

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

``````
``````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)
``````

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
``````

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)>

``````
```defmodule Tax 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

```

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
``````

My solution

```defmodule Tax 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
```

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
``````
```defmodule Taxes 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
```

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
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
``````
``````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.)

``````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
``````

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)
```

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
``````

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
``````

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
``````

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
``````
``````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
``````

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

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.

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)
```

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

``````

@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.

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)
``````
``````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}]
``````
``````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
``````
```defmodule Tax 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
```

I solved differently, Is my solution optimal?

``````
defmodule MyList do
def get_tax([], _tax_rates), do: []
def get_tax([head = [_, _, _] | tail ], tax_rates) do
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)

``````

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
``````
```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
```

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.

```# My solution, it is not elegant though.

# The 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 list:

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: 130, 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.

defmodule Tax do
def add_tax_to(order = [ id: _, ship_to: state, net_amount: net], tax_rates) do
tax_rate = tax_rates[state]
if tax_rate do
total = (1 + tax_rate) * net
else
total = net
end
order ++ [total_amount: total]
end
end

```

Not sure why those are keywords and not maps. Anyways:

```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 }
]

apply_tax = fn(order) ->
%{ship_to: state, net_amount: amount} = order
tax_rate = Keyword.get(tax_rates, state, 0)
Map.put(order, :total_amount, amount * (tax_rate + 1))
end

IO.inspect for order <- orders, do: apply_tax.(order)

```

Here is my solution, pretty straightforward, but not very elixir-like.

``````defmodule Orders do
def apply_taxes tax_rates, orders do
orders |> Enum.map(&(Keyword.put(&1, :total_amount, &1[:net_amount]* (1 + tax_rates[&1[:ship_to]]))))
end
end
``````
``````defmodule ApplyTax do
def to(orders, tax_rates) do
for order = [ _, ship_to: state, net_amount: net ] <- orders,
do: [ { :total_amount, net * (Keyword.get(tax_rates, state, 0) + 1) } | order  ]
end
end
``````

Hi,

This line of code in Dave Thomas’ answer

``````Enum.map(add_total_to(&1, tax_rates))
``````

will raise a CompileError because a ‘&’ is missing right before add_total_to() that is necessary to create the capture and thus be able to use &1.

Another one:

```defmodule Irs do
defp total_amount(o) do
tax_rates = [ NC: 0.075, TX: 0.08 ]
tp = case o[:ship_to] do
:TX -> o[:net_amount] * (1 + tax_rates[:TX])
:NC -> o[:net_amount] * (1 + tax_rates[:NC])
_ -> o[:net_amount]
end
[total_amount: tp ]
end

def taxer(orders) do
orders |> Enum.map(fn o -> o ++ total_amount(o) 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: 130, ship_to: :NC, net_amount: 50.00 ] ]

IO.puts inspect orders
IO.puts inspect Irs.taxer(orders)
```

Solution with Guard Clauses Function And Parameterized Function for applying taxes.

```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 ] ]

defmodule TaxRate do
# Solution with Guard Clauses Function And Parameterized Function for taxes
defp total_amount(tax) when is_float(tax), do: fn x -> x * (1 + tax) end
defp total_amount(_tax)                  , do: fn x -> x end

def orders_with_tax(orders, tax_rates) do
for order = [_, {_, ship_to}, {_, net_amount} ] <- orders, do:
order ++ [ total_amount: total_amount(tax_rates[ship_to]).(net_amount) ]
end
end

orders |> TaxRate.orders_with_tax(tax_rates)
```

My solution:

``````defmodule OrdersWithTax do

def for(orders,tax_rates) do
for order <- orders, tax = Keyword.get(tax_rates,order[:ship_to],0), do: Enum.concat order,[total_amount: (1+tax)*order[:net_amount]]
end

end

OrdersWithTax.for(orders, tax_rates)
``````

It would be with pattern matching like `order = [...] <- orders` more Elixir like but my solution works even if the orders data set is unclean like this:

``````orders = [
[ id: 123, ship_to: :NC, net_amount: 100.00 ],
[ id: 124, should_not: "be_there", 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 ],
[ 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 ] ]
``````

Sort of inline =P but it works out.

``````defmodule Accounting do
def taxable(rates, orders) do
_taxable(rates, orders)
end

defp _taxable(rates, [order | tail]) do
[Keyword.put(order, :total_amount, Keyword.get(order, :net_amount, 1) * (1 + Keyword.get(rates, Keyword.get(order, :ship_to, 0), 0))) | _taxable(rates, tail)]
end

defp _taxable(_, []), do: []
end
``````

I did it without using a module or 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: 130, ship_to: :NC, net_amount:  50.00 ] ]

IO.inspect(for a <- orders do
if (rate = tax_rates[Keyword.get(a,:ship_to)]) != nil do
a ++ [total_amount: Float.round(Keyword.get(a, :net_amount) * (1 + rate),2)]
else
a ++  [total_amount: Keyword.get(a, :net_amount)]
end
end)
```
``````defmodule Comprehensions 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 do
order |> Keyword.put_new(:total_amount, calculate_total(order))
end
end

defp calculate_total(order \\ []) do
tax = @tax_rates |> Keyword.get(order[:ship_to], 0)
order[:net_amount] + tax
end
end
``````

Solution 1 : Built with comprehensions, cond, and keywords list functions

``````defmodule Taxes do
def tax_cal do
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 ]

for order <- orders do

cond do
order[:ship_to] == :NC ->
order ++ [total_amount: (1+tax_rates[:NC]) * order[:net_amount]]

order[:ship_to] == :TX ->
order ++ [total_amount: (1+tax_rates[:TX]) * order[:net_amount]]

true                   ->
order ++ [total_amount: order[:net_amount]]
end
end
end
end
``````

Solution 2 : Built with recursion and lists only

``````
defmodule Part1chap10 do

def test([], [ NC: nico, TX: tixo]), do: []

def test( [[id: ide, ship_to: shipy, net_amount: net_am]|tail], [NC: nico, TX: tixo]) when shipy == :NC do
[[id: ide, ship_to: shipy, net_amount: net_am, total_amount: net_am*(1+nico)]|test( tail, [NC: nico, TX: tixo])]
end

def test( [[id: ide, ship_to: shipy, net_amount: net_am]|tail], [NC: nico, TX: tixo]) when shipy == :TX do
[[id: ide, ship_to: shipy, net_amount: net_am, total_amount: net_am*(1+tixo)]|test( tail, [NC: nico, TX: tixo])]
end

def test( [[id: ide, ship_to: shipy, net_amount: net_am]|tail],[NC: nico, TX: tixo]) when shipy != :NC and shipy != :TX do
[[id: ide, ship_to: shipy, net_amount: net_am, total_amount: net_am+0]|test( tail, [NC: nico, TX: tixo])]
end

end

``````

Here is a working version similar to Dave’s. It uses pattern matching, the Keyword library and comprehensions.

``````
defmodule Orders do

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

def orders_with_total(orders, tax_rates) do
for order <- orders,
end

end

``````

Here is my attempt, learned quite a bit from this exercise. Was not sure how to use the orders ship_to atom value :NC to key into the tax_rates keyword NC:, until I looked at the elixir Keyword documentation where it shows that [A: 1, B: 2] is short for [{:A, 1}, {:B, 2}] so equivalent, I think I am starting to understand this.

``````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 ] ]

defmodule CalcTax do
def calc_tax(_, []), do: []
def calc_tax(tax_rates, [head | tail]) do
end
end

CalcTax.calc_tax(tax_rates, orders)

iex(368)> CalcTax.calc_tax(tax_rates, orders)
[
[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: 130, ship_to: :NC, net_amount: 50.0, total_amount: 53.75]
]

``````
``````defmodule Sales do
def order_totals(orders, tax_rates) do
for order <- orders, do: Enum.into(order, [total_amount: calculate_total_amount(order, tax_rates)])
end

def calculate_total_amount(order,tax_rates) do
(1.0 + Keyword.get(tax_rates, order[:ship_to], 0)) * order[:net_amount]
end
end
``````
``````defmodule Tax do
def order_with_total(orders, tax_rates) do
for order <- orders, into: [], do: get_order_with_total(order, tax_rates)
end

defp get_order_with_total([id: _id, ship_to: state, net_amount: net_amount]= order, tax_rates) do
tax_rate = Keyword.get(tax_rates, state, 0)
Keyword.put(order, :total_amount, net_amount * (1+tax_rate))
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 ] ]

Tax.order_with_total(orders, tax_rates)
``````
You must be logged in to comment