small medium large xlarge

Dave_gnome_head_isolated_pragsmall
16 Jul 2013, 02:25
Dave Thomas (378 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 (56 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 (2 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 (8 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
Generic-user-small
29 Jun 2016, 22:36
Romario López (4 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
You must be logged in to comment