16 Jul 2013, 02:25 Dave Thomas (338 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 ```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) ``` I wish it were that simple…. ↩ 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) `````` 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 ) `````` 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))) `````` 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]] `````` 30 Dec 2013, 18:42 Hugo Pessoa de 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 ``` 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 `````` 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 `````` 04 Aug 2014, 07:54 Patrick Oscity (6 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) `````` You must be logged in to comment