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:

wordBalancing

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)
 
Advertisements