2017-01-08 4 views
4

В настоящее время я пишу веб-словарь для лексики в Вяз. Это требует сортировки списка слов с помощью пользовательского компаратора.Как сравнить несколько полей в 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 
    } 

типа псевдоним WordList = List (Word)

Мои правила для сравнения (в порядке убывания значимости):

  • количество правильных догадок (по возрастанию)
  • номер общие догадки (по убыванию)
  • , когда слово было последним спросил (по возрастанию)
  • при добавлении слова (по убыванию)

Лучший подход, который я мог придумать это:

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 

, который мне не нравится для целый ряд причин:

  • это некрасиво, как ад
  • это требует от меня, чтобы использовать Date.Format.format (от mgold/вяза дата-формате) -с ompare Значения даты (начиная с даты, по-видимому, не comparable)

Есть ли более элегантный/способ вязания для достижения того, чего я хочу?

Update + решение

Как @ "Зимм i48" предложил в их наиболее отличным ответом, вот гораздо более короткая версия, которая использует elm-ordering package:

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

более Элм-иш способ делать такие вещи композиционно, благодаря оператору . Библиотека elm-ordering предоставляет примитивы, необходимые для выполнения таких функций, особенно функции Ordering.byField и Ordering.breakTiesWith.

Что касается дат, я бы посоветовал использовать Date.toTime (результирующие значения сопоставимы).

Бонус: полная реализация вашей функции заказа доступна для тестирования здесь: https://runelm.io/c/xoz. Вы можете видеть, что это намного проще и читабельнее, чем ваше ...

+1

Отлично! Я добавил соответствующую часть вашего решения на свой вопрос, чтобы избежать зависимости от runelm.io - или вы предпочли бы соответственно обновить текст ответа? –

Смежные вопросы