When building Nikeza, I needed a content provider to have adequate space for topics. In addition, I needed the space available (which was two lines) to look reasonably balanced. Hence, I wanted the two lines worth of topics to be close in length.
I was able to implement this requirement:
I wrote the algorithm originally in F#:
let rec organize group1 group2 remainingWords : string list * string list= let next words = match words |> List.tryLast with | Some w -> w | None -> "" let sorted = remainingWords |> List.sortBy(fun (w:string) -> w.Length) let longest = sorted |> next if longest.Length = 0 then (group1, group2) else let updatedWords = sorted |> List.filter(fun w -> w <> longest) let nextLongest = updatedWords |> next if nextLongest.Length = 0 then (group1, longest::group2) else let remaining = updatedWords |> List.filter(fun w -> w <> nextLongest) if remaining |> List.isEmpty then (longest::group1, nextLongest::group2) else remaining |> organize (longest::group1) (nextLongest::group2)
I used the following test:
let result = organize [] [] ["Some big word"; "Some other big word"; "Word"; "F#"; "C#"; "Elm"; "Software Development"; "Web Development"]
The result was the following:
val result : string list * string list = (["C#"; "Word"; "Web Development"; "Software Development"], ["F#"; "Elm"; "Some big word"; "Some other big word"])
I then ported the F# implementation to Elm:
organize : List Topic -> List Topic -> List Topic -> ( List Topic, List Topic ) organize group1 group2 remainingTopics = let next topics = topics |> List.reverse |> List.head sorted = remainingTopics |> List.sortBy (\t -> String.length t.name) in case sorted |> next of Nothing -> ( group1, group2 ) Just longest -> let updatedTopics = sorted |> List.filter (\t -> t /= longest) in case updatedTopics |> next of Nothing -> ( group1, longest :: group2 ) Just nextLongest -> let remaining = updatedTopics |> List.filter (\t -> t /= nextLongest) in if remaining |> List.isEmpty then ( longest :: group1, nextLongest :: group2 ) else remaining |> organize (longest :: group1) (nextLongest :: group2)