lue-bird / elm-keysset / Keys

Multiple key Orderings

Configure what's considered a key inside a KeysSet

Create starting with Keys.for and building with |> Keys.by as shown in the readme examples

You can just ignore the Typed thing but if you're curious → typed-value

create

one key

oneBy : Mapping element toKeyTag key -> Ordering key keyOrderTag -> Keys element (Key element (Order.By toKeyTag keyOrderTag) key N1) N1

Create Keys for single key in case you don't plan on adding more keys.

See also Keys.identity if you want to use the complete element as the key.

FYI, this is equivalent to

Keys.for identity
    |> Keys.by ( identity, mapping ) ordering

identity : Ordering element elementOrderTag -> IdentityKeys element elementOrderTag

Ordering by the element itself.

Short for

Keys.identity order =
    Keys.for identity |> Keys.by ( identity, Map.identity ) order

in KeysSet

import Map
import Order
import Int.Order
import Keys
import KeysSet

intUp : IdentityKeys Int Int.Order.Up
intUp =
    Keys.identity Int.Order.up

KeysSet.fromList intUp [ -1, 5, 5, 8, 7 ]
    --: Emptiable (IdentitySet Int Int.Order.Up) Possibly
    |> KeysSet.toList intUp
--> [ -1, 5, 7, 8 ]


type alias IdentityKeys element elementOrderTag =
Keys element (Key element (Order.By Map.Identity elementOrderTag) element N1) N1

Resulting type of Keys.identity.

import Map
import Order
import Float.Order
import KeysSet exposing (IdentitySet)
import Keys

floatUp : IdentityKeys Float Float.Order.Up
floatUp =
    Keys.identity Float.Order.up

KeysSet.fromList floatUp [ -1.1, 5, 5, 8.7, 7.8 ]
    --: Emptiable (IdentitySet Float Float.Order.Up) Possibly
    |> KeysSet.toList floatUp
--> [ -1.1, 5, 7.8, 8.7 ]

multiple keys

for : keysConstructor -> KeysBeingBuilt element_ completeKeys_ keysConstructor (N.Up0 keyCount_)

Start a keys builder by giving names to the individual keys as arguments using a record

userKeys :
    Keys
        User
        { name : Key User (Order.By User.Name Username.Order) Username N2
        , email : Key User (Order.By User.Email Email.Order) Email N2
        }
        N2
userKeys =
    Keys.for (\email name -> { email = email, name = name })
        |> Keys.by ( .email, User.email, Email.order )
        |> Keys.by ( .name, User.name, Username.order )

Emptiable.empty
    |> KeysSet.insertIfNoCollision userKeys
        { name = Username "ben", email = "ben10@gmx.de" }
    |> KeysSet.insertIfNoCollision userKeys
        { name = Username "mai", email = "ben10@gmx.de" }
        -- not inserted
        -- There's already an element where .email is "ben10@gmx.de"
    |> KeysSet.insertIfNoCollision userKeys
        { name = Username "ben", email = "ben11@gmx.de" }
        -- not inserted
        -- There's already an element where .name is "ben"


type alias KeysBeingBuilt element keysComplete keysConstructor keyCount =
KeysBeingBuiltWithFocus element keysComplete keysConstructor keysConstructor keyCount

Once you supply all the necessary key-Orderings with by, a KeysBeingBuilt is automatically of type Keys.

So if you for example infer the type

KeysBeingBuilt element { yourKeys } { yourKeys } keyCount

you can replace it by

Keys element { yourKeys } keyCount

It's data is unaccessible because you shouldn't be able to

by : ( keysComplete -> Key element (Order.By toKeyTag keyOrderTag) key (N.Add1 keyCountFrom1), Mapping element toKeyTag key ) -> Ordering key keyOrderTag -> KeysBeingBuilt element keysComplete (Key element (Order.By toKeyTag keyOrderTag) key (N.Add1 keyCountFrom1) -> keysConstructedPartially) (N.Up (N.Add1 toKeyCountFrom1) N.To (N.Add1 keyCountFrom1)) -> KeysBeingBuilt element keysComplete keysConstructedPartially (N.Up toKeyCountFrom1 N.To (N.Add1 keyCountFrom1))

Add a key


type alias Keys element keys keyCount =
KeysWithFocus element keys keys keyCount

The type of fully constructed KeysBeingBuilt ready to use.

"infer types" is your friend here

import Keys exposing (Keys, Key)
import Order
import Char.Order
import String.Order
import Emptiable
import N exposing (N2)

userKeys :
    -- just infer this
    Keys
        User
        { name :
            Key
                User
                (Order.By
                    User.Name
                    (String.Order.Earlier (Char.Order.AToZ Order.Tie))
                )
                String
                N2
        , email : Key User (Order.By User.Email Email.DefaultOrder) String N2
        }
        N2
userKeys =
    Keys.for (\name email -> { name = name, email = email } )
        |> Keys.by ( .name, User.name )
            (String.Order.earlier (Char.Order.aToZ Order.tie))
        |> Keys.by ( .email, User.email )
            Email.defaultOrder

Emptiable.empty
    |> KeysSet.insertIfNoCollision userKeys
        { username = "ben", email = ..ben10@gmx.de.. }
    |> KeysSet.insertIfNoCollision userKeys
        { username = "mai", email = ..ben10@gmx.de.. }
        -- not inserted
        -- There's already an element where .email is ..ben10@gmx.de..

What's with those Up N.. To N..? Internally, each key will be assigned an index. that type from bounded-nat preserves the knowledge that each key's index is less than the whole count.


type alias KeysWithFocus element keys focus keyCount =
KeysBeingBuiltWithFocus element keys keys focus (N.On keyCount)

You'll find this type on operations like KeysSet.remove which require you to specify which specific key from the pool of keys elements will be removed by.

If you just have one key, like from oneBy or identity, just supply the keys and everything will work.

If you have multiple keys, use key to "select" one.

alter

key : (keys -> focusNew) -> KeysWithFocus element keys focusOld_ keyCount -> KeysWithFocus element keys focusNew keyCount

You might need this on operations like KeysSet.remove which require you to specify which specific key from the pool of keys elements will be removed by.

If you just have one key, like from oneBy or identity, just supply the keys and everything will work.

If you have multiple keys, use key to "select" one, like

userKeys =
    Keys.for (\name email -> { name = name, email = email } )
        |> Keys.by ( .name, User.name )
            (String.Order.earlier (Char.Order.aToZ Order.tie))
        |> Keys.by ( .email, User.email )
            Email.defaultOrder

users
    |> KeysSet.remove (key .name userKeys) "default"

safe internals

You won't need them if you just want to use KeysSet. ... It's safe to expose this information, tho, so why not make it available :)


type alias Key element orderByTag key keyCount =
Internal.Key element orderByTag key keyCount

By which aspect = key and in which key Order elements should be sorted

transform

toArray : KeysWithFocus element keys_ focus_ keyCount -> ArraySized (( element, element ) -> Basics.Order) (N.Exactly (N.On keyCount))

List all keys in the keys record as an array of functions determining the Order of 2 elements

single key

toKeyWith : KeysWithFocus element keys_ (Key element orderByTag_ key keyCount) keyCount -> element -> key

How to turn the element into the specified key

keyOrderWith : KeysWithFocus element keys_ (Key element orderByTag_ key keyCount) keyCount -> ( key, key ) -> Basics.Order

How to order by the specified key