Stable Partitioning Algorithm With No Side Effects

Sounds like a job for erlang — OTP 29.0.2 (erts 17.0.2)

iex(10)> partitions = [:a, :b, :c]                                   
[:a, :b, :c]
iex(11)> ["foo", "bar", "foobar"] |> Enum.map(&:erlang.phash2(&1, 2)) |> Enum.map(&Enum.at(partitions, &1))
[:a, :a, :b]