# Weight-based random sampling

**URL:** <https://forum.elixirforum.com/t/weight-based-random-sampling/23345>\
**Category:** Questions\
**Tags:** functional-programming\
**Created:** [June 23, 2019, 3:54pm UTC](https://forum.elixirforum.com/t/weight-based-random-sampling/23345 "2019-06-23T15:54:06Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![NobbZ](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/nobbz/32/27235_2.png) [@NobbZ](https://forum.elixirforum.com/u/NobbZ)\
**Post date:** [June 23, 2019, 5:05pm UTC](https://forum.elixirforum.com/t/weight-based-random-sampling/23345/3 "2019-06-23T17:05:26Z")

</div>

Can’t we write `select_item/3` roughly like this, making it effectively a `select_item/2`?

```ex
defp select_item([{name, weight} | _], idx) when idx < weight, do: name
defp select_item([{_, weight} | tail], idx), do: select_item(tail, idx - weight)

```

While also making use of `Enum.reduce/3` in `sum_weights/2` we can make it a `sum_weights/1`:

```elixir
defp sum_weights(items) do
  Enum.reduce(items, 0, fn {_, weight}, sum -> weight + sum end)
end

```

Then we leave `weight_based_choice` nearly untouched (only adjusting calls to the functions we changed):

```elixir
defmodule Demo do
  def weight_based_choice(items) do
    value =
      items
      |> sum_weights()
      |> :rand.uniform()

    select_item(items, value)
  end

  defp select_item([{name, weight} | _], idx) when idx < weight, do: name
  defp select_item([{_, weight} | tail], idx), do: select_item(tail, idx - weight)

  defp sum_weights(items) do
    Enum.reduce(items, 0, fn {_, weight}, sum -> weight + sum end)
  end
end

 1..10 |> Enum.map(fn _ -> Demo.weight_based_choice(items) end) |> IO.inspect
#=> [:bananas, :bananas, :bananas, :bananas, :bananas, :bananas, :bananas, :bananas, :oranges, :bananas]

```

This is probably one of the most idiomatic approaches. We could even implement `select_item/2` in terms of `Enum.reduce_while/3`, but usually this doesn’t add much in terms of readability, also not many people are used to that function…

```ex
defp select_item(items, idx) do
  Enum.reduce_while(items, idx, fn
    {_, weight}, acc when weight < acc -> {:cont, acc - weight}
    {name, weight}, acc -> {:halt, name}
  end
end

```

* * *

PS: Perhaps we need to adjust `<`/`<=`/`>`/`>=` in the guards… I’m writing this mostly freehand…

---

_[View the full topic](https://forum.elixirforum.com/t/weight-based-random-sampling/23345)._
