defmodule Day5 do def run do {rules, updates} = parseFile("data.txt") {validUpdates, invalidUpdates} = Enum.split_with(updates, &updateValid?(&1, rules)) validMiddles = Enum.map(validUpdates, &middleDigit/1) IO.puts("Part 1: #{Enum.sum(validMiddles)}") # sort the invalid ones with a custom sorter that looks for [a, b] in the rules list. if [a, b] exists, sort a before b invalidSorted = Enum.map(invalidUpdates, fn update -> Enum.sort(update, fn first, second -> Enum.member?(rules, [first, second]) end) end) invalidMiddles = Enum.map(invalidSorted, &middleDigit/1) IO.puts("Part 2: #{Enum.sum(invalidMiddles)}") end defp parseFile(file) do [topSection, bottomSection] = File.read!(file) |> String.split("\n\n") rules = String.split(topSection, "\n") |> Enum.map(fn l -> String.split(l, "|") end) updates = String.split(bottomSection, "\n") |> Enum.drop(-1) |> Enum.map(fn l -> String.split(l, ",") end) {rules, updates} end defp updateValid?(update, rules) do Enum.reduce_while(rules, true, fn [left, right], acc -> # if it doesn't contain both, or it does and they're in the right order if !(Enum.member?(update, left) and Enum.member?(update, right)) or Enum.find_index(update, &(&1 == left)) < Enum.find_index(update, &(&1 == right)) do # continue and try the next one {:cont, acc} else # otherwise halt the reduction, this update is invalid {:halt, false} end end) end defp middleDigit(update) do middle = trunc(length(update) / 2) String.to_integer(Enum.at(update, middle)) end end Day5.run()