How to compare multiple fields in Elm?

I am currently writing web vocabulary in Elm. This requires sorting a list of words using a custom comparator.

Type I want to sort:

type alias Word =
  { id: Int
  , sourceWord: String
  , targetWord: String
  , numTries: Int
  , numCorrect: Int
  , createdAt: Maybe Date    -- might be empty, therefore wrapped in Maybe 
  , lastAskedAt: Maybe Date  -- might be empty, therefore wrapped in Maybe
  }

enter the alias WordList = List (Word)

My comparison rules (in decreasing order of importance):

  • number of correct guesses (asc)
  • number of common guesses (desc)
  • when the last word was asked (asc)
  • when the word was added (desc)

The best approach I could come up with is this:

compareWords: Word -> Word -> Basics.Order
compareWords w1 w2 = 
  let
      dateToComparable d = Date.Format.format "%Y-%m-%d" d 
      orderNumCorrect = compare w1.numCorrect w2.numCorrect 
      orderNumTries = compare w2.numTries w1.numTries -- switch ordering to sort descending
      orderLastAskedAt = case (w1.lastAskedAt, w2.lastAskedAt) of
        (Just a1, Just a2) -> compare (dateToComparable a1) (dateToComparable a2)
        (Nothing, Just _)  -> Basics.LT
        (Just _,  Nothing) -> Basics.GT
        (Nothing, Nothing) -> Basics.EQ 
      orderCreatedAt = case (w2.createdAt, w1.createdAt) of -- switch ordering to sort descending
        (Just a1, Just a2) -> compare (dateToComparable a1) (dateToComparable a2)
        (Nothing, Just _)  -> Basics.LT
        (Just _,  Nothing) -> Basics.GT
        (Nothing, Nothing) -> Basics.EQ 
   in
      case orderNumCorrect of
        Basics.EQ -> case orderNumTries of
          Basics.EQ -> case orderLastAskedAt of
            Basics.EQ -> orderCreatedAt
            _ -> orderLastAskedAt
          _ -> orderNumTries 
        _ -> orderNumCorrect

which I don’t like for a number of reasons:

  • It's ugly like hell
  • I need to use Date.Format.format(from mgold / elm-date-format) to compare date values ​​(since the date is apparently not comparable)

Is there a more elegant / Elm -ish way to achieve what I want?

Update + Solution

@ "Zimm i48" , , elm:

dateToComparable : Maybe Date -> Time
dateToComparable =
    Maybe.map Date.toTime >> Maybe.withDefault 0 

compareWords : Ordering Word
compareWords =
    Ordering.byField .numCorrect
        |> Ordering.breakTiesWith (Ordering.byField (.numTries >> negate))
        |> Ordering.breakTiesWith (Ordering.byField (.lastAskedAt >> dateToComparable))
        |> Ordering.breakTiesWith
            (Ordering.byField (.createdAt >> dateToComparable >> negate))
+4
1

Elm-ish , |>. elm-ordering , , Ordering.byField Ordering.breakTiesWith.

, Date.toTime ( ).

: : https://runelm.io/c/xoz. , ...

+4

Source: https://habr.com/ru/post/1666158/


All Articles