My Favorite Functions and Patterns: group_by

Thank you for joining me as I share my favorite functions and patterns in functional programming. Today’s focus is on the function group_by. Previously, I published Function: with Statement and Function: Curry. This is the third installation in the series. For all of this week’s functions and patterns,  visit the Index.

I encountered this little gem while pair-programming with a colleague. It affords much potential in the domain of complex sorting operations.

Say we’ve a large list of unsorted, unordered exam results:

def list_exam_results do
  [
    %{name: "Zelda", score: 56},
    %{name: "Eddy", score: 58},
    %{name: "Jack", score: 69},
    %{name: "Abby", score: 84},
    %{name: "Lily", score: 96}
  ]
end

First, we want to segregate records by letter grade (e.g., A, B, C, D, F), and then, for each group, we want to order results by student name. Elixir’s Enum.group_by is perfect.

Implementations of group_by generally take a list of items and then a predicate. Our predicate might look something like:

def by_grade(%{score: score}) do
  cond do
    score < 65 -> "F"
    score < 70 -> "D"
    score < 80 -> "C"
    score < 90 -> "B"
    true -> "A"
  end
end

Which will take any of our records from list_exam_results and return a letter grade. On its own:

Enum.group_by(list_exam_results(), &by_grade(&1))

Will return:

%{
  "A" => [%{name: "Lily", score: 96}],
  "B" => [%{name: "Abby", score: 84}],
  "D" => [%{name: "Jack", score: 69}],
  "F" => [%{name: "Zelda", score: 56}, %{name: "Eddy", score: 58}]
}

We’ve now positioned our data so that its subsets can be sorted by letter grade. Our final implementation might resemble:

def prepare_records(coll \\ list_exam_results()) do
  coll
  |> Enum.group_by(&by_grade(&1))
  |> Enum.map(fn {_letter_grade, subset} -> Enum.sort(subset) end)
end

Which will give us:

[
  [%{name: "Lily", score: 96}],
  [%{name: "Abby", score: 84}],
  [%{name: "Jack", score: 69}],
  [%{name: "Eddy", score: 58}, %{name: "Zelda", score: 56}]
]

With Zelda‘s record appearing after Eddy.

Resources

For more Functional Programming tips:

Check out Function: `with` Statement
Check out Function: ‘curry`

Read more from our engineering, design, product, sales, and growth teams.

Check out our Open Source repositories here.

More Posts by Jonathan Walters: