So
defmodule Demo do
def weight_based_choice(items) do
value =
items
|> Enum.reduce(0, &acc_weight/2)
|> :rand.uniform()
select_item(items, value)
end
# base cases
defp select_item([{name, _}], _),
do: name
defp select_item([{name, weight} | _], index) when weight >= index,
do: name
# recursive case
defp select_item([{_, weight} | tail], index),
do: select_item(tail, index - weight)
defp acc_weight({_, weight}, total),
do: total + weight
end
items = [apples: 10, oranges: 30, bananas: 60]
items
|> Demo.weight_based_choice()
|> inspect()
|> IO.puts()
or
defmodule Demo do
def weight_based_choice(items) do
marker =
items
|> Enum.reduce(0, &acc_weight/2)
|> :rand.uniform()
Enum.reduce_while(items, marker, &item_below_marker/2)
end
defp item_below_marker({_name, weight}, acc) when weight < acc,
do: {:cont, acc - weight}
defp item_below_marker({name, _weight}, _acc),
do: {:halt, name}
defp acc_weight({_, weight}, total),
do: total + weight
end
items = [apples: 10, oranges: 30, bananas: 60]
items
|> Demo.weight_based_choice()
|> inspect()
|> IO.puts()
While also making use of
Enum.reduce/3
People tend to have familiarity with reduce from other languages - when learning I feel that resorting to Enum immediately is like heading straight for the power-tools even for the smallest job. To a certain degree choosing reduce when a simple recursive function will do feels a bit like “premature pessimization” especially as recursion is how iteration is accomplished in Elixir/Erlang - but I know I’m in the minority there.


















