如何比较榆木中的多个字段?

Fra*_*itt 5 comparison elm

我目前正在用榆木写一个基于网络的词汇培训师。这需要通过自定义比较器对单词列表进行排序。

我要排序的类型是:

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
  }
Run Code Online (Sandbox Code Playgroud)

类型别名WordList =列表(Word)

我的比较规则是(按重要性降序排列):

  • 正确猜测数(升)
  • 总体猜测数(降)
  • 上次询问单词的时间(asc)
  • 添加单词时(降序)

我能想到的最好方法是:

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
Run Code Online (Sandbox Code Playgroud)

我不喜欢的原因有很多:

  • 丑陋如地
  • 它要求我使用Date.Format.format(从mgold / elm-date-format)比较Date值(因为Date显然不是comparable

有没有更优雅/榆木的方式来实现我想要的?

更新+解决方案

正如@“ Zimm i48”在其最出色的答案中所建议的,这是一个使用elm-ordering软件包的简短版本:

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))
Run Code Online (Sandbox Code Playgroud)

Zim*_*i48 5

归功于|>操作员,一种更为Elm-ish的方式来完成这种事情是有条理的。在榆树排序库提供的原语,你需要做这样的事情,尤其是Ordering.byFieldOrdering.breakTiesWith功能。

至于日期,我的建议是使用Date.toTime(结果值是可比较的)。

奖励:订购功能的完整实现可在此处进行测试:https : //runelm.io/c/xoz。您会发现它比您更简单,更易读...