Comparing things
Build tagged compare functions for basic types, records, type choices.
All without comparable, number, ...
import Order exposing (Ordering)
import Int.Order
import Char.Order
import String.Order
import Map exposing (Mapping)
type FirstName
-- no exposing (..)
= FirstName
firstName : Mapping User FirstName String
firstName =
Typed.tag FirstName (\(User user) -> user.firstName)
type LastName
-- no exposing (..)
= LastName
lastName : Mapping User LastName String
lastName =
Typed.tag LastName (\(User user) -> user.lastName)
type Age
-- no exposing (..)
= Age
age : Mapping User Age String
age =
Typed.tag Age (\(User user) -> user.age)
type User
= User
{ firstName : String
, lastName : String
, age : Int
}
type alias NameOrder =
String.Order.Earlier
(Char.Order.AToZ Char.Order.LowerUpper)
type alias Order =
Order.NextOnTie
(Order.By LastName NameOrder)
(Order.NextOnTie
(Order.By FirstName NameOrder)
(Order.By Age Order.Int.Up)
)
order : Ordering User Order
order =
Order.by lastName
(String.Order.earlier
(Char.Order.aToZ Char.Order.lowerUpper)
)
|> Order.onTie
(Order.by .firstName
(String.Order.earlier
(Char.Order.aToZ Char.Order.lowerUpper)
)
)
|> Order.onTie
(Order.by .age Int.Order.up)
[ User { firstName = "Andy", lastName = "Baldwin", age = 90 }
, User { firstName = "Bert", lastName = "Anderson", age = 23 }
, User { firstName = "Alec", lastName = "Anderson", age = 8 }
, User { firstName = "Alec", lastName = "Anderson", age = 100 }
]
|> List.sortWith (Order.with userOrder)
--> [ { firstName = "Alec", lastName = "Anderson", age = 8 }
--> , { firstName = "Alec", lastName = "Anderson", age = 100 }
--> , { firstName = "Bert", lastName = "Anderson", age = 23 }
--> , { firstName = "Andy", lastName = "Baldwin", age = 90 }
--> ]a < b ⇆ a > b
import Char.Order
import String.Order
[ "b", "c", "a" ]
|> (List.sortWith << Order.with)
(String.Order.earlier
(Char.Order.aToZ Order.tie)
)
--> [ "a", "b", "c" ]
[ "b", "c", "a" ]
|> (List.sortWith << Order.with)
(String.Order.earlier
(Char.Order.aToZ Order.tie)
|> Order.reverse
)
--> [ "c", "b", "a" ]Tag for reverse
Tag wrapper for Reverse
Order by a transformed value
import Typed
import String.Order
import Char.Order
import Map exposing (Mapping)
type Name
= Name
name : Mapping { name : String } Name String
name =
Typed.tag Name .name
[ { name = "Bo" }, { name = "Amy" }, { name = "Cam" } ]
|> (List.sortWith << Order.with)
(Order.by name
(String.Order.earlier (Char.Order.aToZ Order.tie))
)
--> [ { name = "Amy" }, { name = "Bo" }, { name = "Cam" } ]
{-| `Order` `Tuple.first`, then on tie `Tuple.second`
Order.tuple ( Int.Order.up, Int.Order.up ) ( 0, 2 ) ( 0, -2 )
--→ GT
-}
tuple :
( Ordering part0 part0OrderTag
, Ordering part1 part1OrderTag
)
-> Ordering
( part0, part1 )
(Order.OnTieNext
(Order.By Tuple.Map.Part0 part0OrderTag)
(Order.By Tuple.Map.Part1 part1OrderTag)
)
tuple ( part0Order, part1Order ) =
Order.by Tuple.Map.part0 part0Order
|> Order.onTie
(Order.by Tuple.Map.part1 part1Order)import Char.Order
import Order exposing (Ordering)
import String.Order
type User
= User { name : String }
userNameOrder : Ordering Username
userNameOrder =
Order.by (\(User user) -> user)
(Order.by .name
(String.Order.earlier
(Char.Order.aToZ Char.Order.lowerUpper)
)
)type Case
= Lower
| Upper
{-| `'A' < 'a'`
-}
upperLower : Ordering Case
upperLower =
Order.by
(\case_ ->
case case_ of
Upper ->
0
Lower ->
1
)
Int.Order.upneat!
Prioritize the Ordering by one aspect
and break ties with the following
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 (Order.OnTieNext (Order.By Suite SuiteOrder) (Order.By Value ValueOrder))
cardOrder =
Order.by suite suiteOrder
|> Order.onTieNext
(Order.by value valueOrder)
type SuiteToInt
= SuiteToInt
suiteToInt : Mapping Suite SuiteToInt Int
suiteToInt =
Typed.tag SuiteOrder
(\suite ->
case suite of
Clubs ->
0
Hearts ->
1
Diamonds ->
2
Spades ->
3
)
type SuiteOrder =
Order.By SuiteToInt Int.Order.Up
suiteOrder : Ordering Suite SuiteOrder
suiteOrder =
Order.by suiteToInt Int.Order.uponTie can also be used to order different variants and values → see on
Tag for onTie
Tag wrapper for OnTieNext
Try ordering by a given parsed value.
In comparison to Order.by, mapping can produce Nothing in which case any Just value is greater.
Continue by adding all possibilities with Order.onTie
-- 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 }
type ToNormal
= ToNormal
normal : Mapping Card ToNormal (Maybe CardNormal)
normal =
Typed.tag ToNormal
(\card ->
case card of
Normal normalCard ->
Just normalCard
Joker ->
Nothing
)
type alias NormalOrder =
Order.OnTieNext
(Order.By Suite SuiteOrder)
(Order.By Value ValueOrder)
normalOrder : Ordering CardNormal NormalOrder
normalOrder =
Order.by suite suiteOrder
|> Order.onTie
(Order.by value valueOrder)
order : Ordering Card (Order.On ToNormal NormalOrder)
order =
Order.on toNormal normalOrder
type Normal
= Normal
toNormal : Mapping Card Normal (Maybe CardNormal)
toNormal =
\card ->
case card of
Normal normal ->
normal |> Just
Joker ->
Nothing-- Advanced territory --
Recursive types need special treatment so we don't run into recursive types:
Recursive Orderings need a separate tag, e.g.
import Map
import Order
import Typed
type alias Earlier elementOrder =
Order.OnTieNext
(Order.On Empty Order.Tie)
(Order.On
Cons
(Order.OnTieNext
(Order.By ConsElement elementOrder)
(Order.By ConsList ( EarlierRecursive, elementOrder ))
)
)
type Empty
= Empty
toEmpty : Mapping (List element) Empty (Maybe ())
toEmpty =
Typed.tag Empty
(\list ->
case list of
[] ->
Just ()
_ :: _ ->
Nothing
)
type Cons
= Cons
toCons : Mapping (List element) Cons (Maybe { element : element, list : List element })
toCons =
Typed.tag Cons
(\list ->
case list of
[] ->
Nothing
element :: list ->
Just { element = element, list = list }
)
type ConsElement
= ConsElement
consElement : Mapping { element : element, list : List element } ConsList (List element)
consElement =
Typed.tag ConsList .list
type ConsList
= ConsList
consList : Mapping { element : element, list : List element } ConsList (List element)
consList =
Typed.tag ConsList .list
type EarlierRecursive
= EarlierRecursive
order : Ordering element elementOrder -> Ordering (List element) (Earlier elementOrder)
order elementOrder =
let
listRecursiveOrder : () -> Ordering (List element) ( EarlierRecursive, elementOrder )
listRecursiveOrder () =
Typed.mapToWrap EarlierRecursive order
in
Order.on toEmpty Order.tie
|> Order.onTie (Order.on toCons Order.by)a bit much boilerplate.
Convert to a function in the form a -> a -> Order
that can be used to sortWith, make comparisons, ...
cardsSort : List Card -> List Card
cardsSort =
List.sortWith (Order.with cardOrder)
cardOrder : Ordering Cardmatthewsj/elm-orderingbyField: byField Tuple.first, ... are also possibleisOrdered, both byField & byFieldWith, ...... |> breakTiesWith ... chain instead of onTieNext [ ... ]list, maybe, ... orderingsTSFoster/elm-comparelist, maybe, ... compare operationsrtfeldman/elm-sorter-experimenttype → more verboselist, maybe, ... sortersSort.Set & Sort.Dict are very nice! You can even use Sort.Set.empty (Sorter.custom Order.x) if you wantednikita-volkov/: Typeclasses-Classes-Comparisonint,float,comparable & map)HashingContainers.HashSet,
HashingContainers.HashDict
are nice!
How 2 thing compare to each other. An
Orderingcan be used tosortWith, make comparisons, ...