Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,46 @@ defmodule Enum do
{:lists.reverse(list), acc}
end

@doc """
Returns a map with keys as unique elements of `enumerable` and values
as the count of every element.

## Examples

iex> Enum.frequencies(~w{ant buffalo ant ant buffalo dingo})
%{"ant" => 3, "buffalo" => 2, "dingo" => 1}

"""
@doc since: "1.10.0"
@spec frequencies(t) :: map
def frequencies(enumerable) do
reduce(enumerable, %{}, fn key, acc ->
Map.update(acc, key, 1, &(&1 + 1))
end)
end

@doc """
Returns a map with keys as unique elements given by `key_fun` and values
as the count of every element.

## Examples

iex> Enum.frequencies_by(~w{aa aA bb cc}, &String.downcase/1)
%{"aa" => 2, "bb" => 1, "cc" => 1}

iex> Enum.frequencies_by(~w{aaa aA bbb cc c}, &String.length/1)
%{3 => 2, 2 => 2, 1 => 1}

"""
@doc since: "1.10.0"
@spec frequencies_by(t, (element -> any)) :: map
def frequencies_by(enumerable, key_fun) when is_function(key_fun) do
reduce(enumerable, %{}, fn entry, acc ->
key = key_fun.(entry)
Map.update(acc, key, 1, &(&1 + 1))
Copy link
Member

@michalmuskala michalmuskala Oct 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given it's a library function and it should be as efficient as possible, could we manually expand the call to Map.update to avoid the fun overhead? It's tiny, but things like that add up, especially in library functions.

end)
end

@doc """
Splits the `enumerable` into groups based on `key_fun`.

Expand Down
10 changes: 10 additions & 0 deletions lib/elixir/test/elixir/enum_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,16 @@ defmodule EnumTest do
assert Enum.flat_map_reduce([1, 2, 3], 0, &{[&1, &2], &1 + &2}) == {[1, 0, 2, 1, 3, 3], 6}
end

test "frequencies/1" do
assert Enum.frequencies([]) == %{}
assert Enum.frequencies(~w{a c a a c b}) == %{"a" => 3, "b" => 1, "c" => 2}
end

test "frequencies_by/2" do
assert Enum.frequencies_by([], fn _ -> raise "oops" end) == %{}
assert Enum.frequencies_by([12, 7, 6, 5, 1], &Integer.mod(&1, 2)) == %{0 => 2, 1 => 3}
end

test "group_by/3" do
assert Enum.group_by([], fn _ -> raise "oops" end) == %{}
assert Enum.group_by([1, 2, 3], &rem(&1, 2)) == %{0 => [2], 1 => [1, 3]}
Expand Down