lue-bird/elm-linear-direction/5.3.0

NOTE: the latest version is 11.2.0

Order

Comparing 2 things.

type alias Ordering orderable =
orderable -> orderable -> Order

An Ordering to sortWith, make comparisons, and so on:

cardsSort : List Card -> List Card
cardsSort =
    List.sortWith cardOrder


cardOrder : Ordering Card

defaults

tie : Ordering orderable_

Always EQ independent of the arguments.

int : Ordering Int

Order Ints.

Order.int 40 2
--> GT
float : Ordering Float

Order Floats.

Order.float 0.1 -0.2
--> GT
tuple : ( Ordering part0, Ordering part1 ) -> Ordering ( part0, part1 )

Order Tuple.first, then on tie Tuple.second.

Order.tuple ( Order.int, Order.int ) ( 0, 2 ) ( 0, -2 )
--> GT

Try implementing tuple with downOnTie and by if you want :)

maybe : Ordering content -> Ordering (Maybe content)

Order Nothing < Just

Order.maybe Order.int (Just -99999) Nothing
--> GT
list : Ordering element -> Ordering (List element)

Order Lists by elements first to last.

Order.list Order.int
    [ 11, 22, 33, 188 ]
    [ 11, 22, 34 ]
--> LT
char : { case_ : Ordering Case } -> Ordering Char

Order Chars.

Order.char { case_ = Order.upperLower } 'b' 'D'
--> LT


Order.char { case_ = Order.upperLower } 'l' 'L'
--> GT
string : { case_ : Ordering Case } -> Ordering String

Order Strings by Chars first to last, specifying the Case-Ordering:

Order.string { case_ = Order.lowerUpper }
    "hello, human!"
    "hello, Human"
--> LT

For string-number chunked text, you can use mcordova47/: NaturalOrdering.compare

NaturalOrdering.compare "abc2.tff" "abc10.tff"
--→ LT

Personally, I'd just store the String as something like

type TextChunked
    = TextChunked ( String, Maybe (List ( Int, String )) )

and order that to avoid converting too often.

casing

type Case
= Lower
| Upper
upperLower : Ordering Case

'A' < 'a'

lowerUpper : Ordering Case

'a' < 'A'

"transform"

by :
( orderable -> orderableMapped, Ordering orderableMapped )
-> Ordering orderable

Order by a transformed value. Examples:

access fields in a record

[ { name = "Bo" }, { name = "Amy" }, { name = "Cam" } ]
    |> List.sortWith
        (Order.by ( .name, Order.string { case_ = Order.tie } ))
--> [ { name = "Amy" }, { name = "Bo" }, { name = "Cam" } ]

unwrap a type union constructor

import Order exposing (Ordering)


type User
    = User { name : String }


userNameOrder : Ordering Username
userNameOrder =
    Order.by
        ( \(User user) -> user
        , Order.by ( .name, Order.string { case = Order.lowerUpper } )
        )

rank an enum

type Case
    = Lower
    | Upper


{-| `'A' < 'a'`
-}
upperLower : Ordering Case
upperLower =
    Order.by
        ( \case_ ->
            case case_ of
                Upper ->
                    0


                Lower ->
                    1
        , Order.int
        )

rank a union

module Card exposing (Card(..), CardNormal, order)


import Order exposing (Ordering)
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)


type Card
    = Normal CardNormal
    | Joker


type alias CardNormal =
    RecordWithoutConstructorFunction
        { value : Value, suite : Suite }


normalOrder : Ordering CardNormal
normalOrder =
    Order.downOnTie
        [ Order.by ( .suite, suiteOrder )
        , Order.by ( .value, valueOrder )
        ]


order : Ordering Card
order =
    \card0 card1 ->
        case ( card0, card1 ) of
            -- match all variants with _values_
            ( Normal normal0, Normal normal1 ) ->
                normalOrder normal0 normal1


            _ ->
                -- sort others according to tag "rank"
                Order.by
                    ( \card ->
                        case card of
                            Normal _ ->
                                0


                            Joker ->
                                1
                    , Order.int
                    )
                    card0
                    card1

Use ↑ once your type union grows to have lots of variants where exhaustive matching has n^2 branches

For simple type unions with only 2 variants:

order : Ordering Card
order =
    \card0 card1 ->
        case ( card0, card1 ) of
            -- match all variants with _values_
            ( Normal normal0, Normal normal1 ) ->
                normalOrder normal0 normal1


            ( Normal _, Joker ) ->
                GT


            ( Joker, Normal _ ) ->
                LT


            ( Joker, Joker ) ->
                EQ

Pretty neat stuff!

downOnTie : List (Ordering orderable) -> Ordering orderable

Prioritize the Ordering by one aspect and break ties with the next.

import List.Extra as List
import Order exposing (Ordering)
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)


type alias Card =
    RecordWithoutConstructorFunction
        { suite : Suite, value : Value }

(RecordWithoutConstructorFunction tricks elm into not creating a Card function)

type Suite
    = Clubs
    | Hearts
    | Diamonds
    | Spades


type Value
    = Two
    | Three
    | Four
    | Five
    | Six
    | Seven
    | Eight
    | Nine
    | Ten
    | Jack
    | Queen
    | King
    | Ace


cardOrder : Ordering Card
cardOrder =
    Order.downOnTie
        [ Order.by ( .suite, suiteOrder )
        , Order.by ( .value, valueOrder )
        ]


suiteOrder : Ordering Suite
suiteOrder =
    Order.by
        ( \suite ->
            case suite of
                Clubs ->
                    0


                Hearts ->
                    1


                Diamonds ->
                    2


                Spades ->
                    3
        , Order.int
        )

alter

reverse : Ordering orderable -> Ordering orderable

a < b ⇆ a > b

[ "b", "c", "a" ]
    |> List.sortWith
        (Order.string { case_ = Order.tie })
--> [ "a", "b", "c" ]


[ "b", "c", "a" ]
    |> List.sortWith
        (Order.string { case_ = Order.tie }
            |> Order.reverse
        )
--> [ "c", "b", "a" ]

prior art