Skip to content

Instantly share code, notes, and snippets.

@grufino
Last active August 20, 2019 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grufino/d51dc114828f92fc6c576b5eaed1fc90 to your computer and use it in GitHub Desktop.
Save grufino/d51dc114828f92fc6c576b5eaed1fc90 to your computer and use it in GitHub Desktop.
Module to give averages based on a list of lists of numbers.
defmodule MissingData do
@doc """
Example input:
> MissingData.average_list([[0, 10, 20], [20, 10, 0], [10, 10, 10]])
Example output:
> [15, 10, 15]
"""
def average_list(list_of_lists) do
if have_same_sizes?(list_of_lists) do
flattened_indexed_list = Enum.flat_map(list_of_lists, &Enum.with_index/1)
#This sums all lists and also how many items were summed in a tuple
non_zero_averages =
flattened_indexed_list
|> prepare_for_averaging
#This replaces the zeroes for average tuples found, then sorts, then finally averages.
Enum.reduce(flattened_indexed_list, non_zero_averages, fn {value, index}, acc ->
Map.update(acc, index, value, & &1)
end)
|> Enum.sort_by(fn {k, _v} -> k end)
|> Enum.map(&average/1)
else
:error
end
end
def prepare_for_averaging(list) when is_list(list) do
prepare_for_averaging(list, %{})
end
def prepare_for_averaging([], acc) do
acc
end
def prepare_for_averaging([{0, _} | tl], acc) do
prepare_for_averaging(tl, acc)
end
def prepare_for_averaging([{value, index} | tl], acc) do
prepare_for_averaging(
tl,
Map.update(acc, index, {value, 1}, fn {existing_value, divisions} ->
{value + existing_value, divisions + 1}
end)
)
end
def have_same_sizes?(list_of_lists) do
{min, max} =
list_of_lists
|> Enum.map(&length/1)
|> Enum.min_max()
min == max
end
def average({_k, 0}), do: 0
def average({_k, {value, divisions}}), do: value / divisions
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment