A dictionary mapping unique keys to values. Similar and based on Dict but without restriction on comparable keys.
Insert, remove, and query operations all take O(log n) time.
When writing a function for conversion from the type you want to use for keys to comparable it's very important to make sure every distinct member of type k produces different value in set o of comparables.
Take for instance those two examples:
We can use Bool as a key for our Dict (No matter how unpractical it might seem)
boolToInt : Bool -> Int
boolToInt bool =
case bool of
False -> 0
True -> 1
empty boolToInt
|> insert True "foo"
|> get True
--> Just "foo"or Maybe String.
comparableKey : Maybe String -> (Int, String)
comparableKey maybe =
case maybe of
Nothing -> (0, "")
Just str -> (1, str)
empty comparableKey
|> insert (Just "foo") 42
|> get (Just "foo")
--> Just 42Note that we give Int code to either constructor and in Case of Nothing we default to "" (empty string).
There is still a difference between Nothing and Just "" (Int value in the pair is different).
In fact, you can "hardcode" any value as the second member of the pair
in case of nothing but empty string seems like a reasonable option for this case.
Generally, this is how I would implement toComparable function for most of your custom data types.
Have a look at the longest constructor,
Define tuple where the first key is int (number of the constructor)
and other are types within the constructor and you're good to go.
Check equality of two AnyDicts
* returns `True` if AnyDicts are equal
* returns `False` if AnyDicts are not equalCreate an empty dictionary by suppling function used for comparing keys.
Note that it's important to make sure every key is turned to different comparable. Otherwise keys would conflict and overwrite each other.
Create a dictionary with one key-value pair.
Note that it's important to make sure every key is turned to different comparable. Otherwise keys would conflict and overwrite each other.
Insert a key-value pair into a dictionary. Replaces value when there is a collision.
Update the value of a dictionary for a specific key with a given function.
Remove a key-value pair from a dictionary. If the key is not found, no changes are made.
Remove all entries from AnyDict.
Useful when you need to create new empty AnyDict using same comparable function for key type.
Determine if a dictionary is empty.
isEmpty (empty identity)
--> True
singleton 1 "foo" identity
|> isEmpty
--> FalseGet the value associated with a key. If the key is not found, return Nothing. This is useful when you are not sure if a key will be in the dictionary.
type Animal = Cat | Mouse | Dog
animalToInt : Animal -> Int
animalToInt animal =
case animal of
Cat -> 0
Mouse -> 1
Dog -> 2
animals : AnyDict Int Animal String
animals =
[ (Cat, "Tom"), (Mouse, "Jerry") ]
|> fromList animalToInt
get Cat animals
-> Just "Tom"
get Mouse animals
--> Just "Jerry"
get Dog animals
--> NothingGet a key associated with key.
This is useful in case of AnyDict because
some parts of a key might not be used
for generating comparable.
This function allows quering AnyDict with old
key to obtain updated one in such cases.
Find out if there is any instance of something in a Dictionary.
type Animal = Cat | Mouse | Dog
animalToInt : Animal -> Int
animalToInt animal =
case animal of
Cat -> 0
Mouse -> 1
Dog -> 2
animals : AnyDict Int Animal String
animals =
[ (Cat, "Tom"), (Mouse, "Jerry") ]
|> fromList animalToInt
isACat : Animal -> String -> Bool
isACat animal _ =
case animal of
Cat -> True
_ -> False
any isACat animals
--> TrueFind if all key value paris in Dictionary match a predicate.
type Animal = Cat | Mouse | Dog
animalToInt : Animal -> Int
animalToInt animal =
case animal of
Cat -> 0
Mouse -> 1
Dog -> 2
animals : AnyDict Int Animal String
animals =
[ (Cat, "Tom"), (Mouse, "Jerry") ]
|> fromList animalToInt
aristocats : AnyDict Int Animal String
aristocats =
[ (Cat, "Marie") ]
|> fromList animalToInt
isACat : Animal -> String -> Bool
isACat animal _ =
case animal of
Cat -> True
_ -> False
all isACat animals
--> False
all isACat aristocats
--> TrueGet all of the keys in a dictionary, sorted from lowest to highest.
Get all of the values in a dictionary, in the order of their keys.
Convert a dictionary into an association list of key-value pairs, sorted by keys.
Convert an association list into a dictionary.
Note that it's important to make sure every key is turned to different comparable. Otherwise keys would conflict and overwrite each other.
Apply a function to all values in a dictionary.
Fold over the key-value pairs in a dictionary, in order from lowest key to highest key.
Fold over the key-value pairs in a dictionary, in order from highest key to lowest key.
Keep a key-value pair when it satisfies a predicate.
Partition a dictionary according to a predicate. The first dictionary contains all key-value pairs which satisfy the predicate, and the second contains the rest.
Apply a function that may or may not succeed to all entries in a dictionary, but only keep the successes.
type Animal = Cat | Mouse | Dog
animalToInt : Animal -> Int
animalToInt animal =
case animal of
Cat -> 0
Mouse -> 1
Dog -> 2
animals : AnyDict Int Animal String
animals =
[ (Cat, "Tom"), (Mouse, "Jerry") ]
|> fromList animalToInt
onlyTom : AnyDict Int Animal String
onlyTom =
[ (Cat, "Tom") ]
|> fromList animalToInt
getCatName : Animal -> String -> Maybe String
getCatName animal name =
case animal of
Cat -> Just name
_ -> Nothing
filterMap getCatName animals == onlyTom
--> TrueCombine two dictionaries. If there is a collision, preference is given to the first dictionary.
Keep a key-value pair when its key appears in the second dictionary. Preference is given to values in the first dictionary.
Keep a key-value pair when its key does not appear in the second dictionary.
The most general way of combining two dictionaries. You provide three accumulators for when a given key appears:
Only in the left dictionary. In both dictionaries. Only in the right dictionary.
Takes a key-fn and a list.
Creates an AnyDict which maps the key to a list of matching elements.
Convert AnyDict to plain dictionary with comparable keys.
Decode a JSON object into an AnyDict.
This encoder is limitted for cases where JSON representation
for a given type is an JSON Object. In JSON, object keys must be of type
String.
If you need to decode different representation into AnyDict value,
just use primitive Decoder types directly and map AnyDict constructors
over these.
import Json.Decode
type Key = Foo | Bar
fromString : String -> Key
fromString str =
case str of
"foo" -> Foo
_ -> Bar
toString : Key -> String
toString key =
case key of
Foo -> "foo"
Bar -> "bar"
type alias Data = AnyDict String Key Int
dataDecoder : Json.Decode.Decoder Data
dataDecoder =
decode (\str _ -> fromString str) toString Json.Decode.int
Json.Decode.decodeString dataDecoder "{\"foo\":1,\"bar\":2}"
|> Result.map toList
--> Ok [(Bar, 2), (Foo, 1)]Decode a JSON object into an AnyDict.
This variant of decode allows you to fail with error while parsing key from String. In such case whole Dict decoding will fail.
import Json.Decode
type Key = Foo | Bar
fromString : String -> Result String Key
fromString str =
case str of
"foo" -> Ok Foo
"bar" -> Ok Bar
_ -> Err <| "Unknown key " ++ str
toString : Key -> String
toString key =
case key of
Foo -> "foo"
Bar -> "bar"
type alias Data = AnyDict String Key Int
dataDecoder : Json.Decode.Decoder Data
dataDecoder =
decode_ (\str _ -> fromString str) toString Json.Decode.int
Json.Decode.decodeString dataDecoder "{\"foo\":1,\"bar\":2}"
|> Result.map toList
--> Ok [(Bar, 2), (Foo, 1)]
Json.Decode.decodeString dataDecoder "{\"foo\":1,\"baz\":2}"
|> Result.map toList
--> Json.Decode.decodeString (Json.Decode.fail "Unknown key baz") "{}"Decode an AnyDict from a JSON list of tuples.
import Json.Decode as Decode
import Json.Encode as Encode
type alias Person = {first : String, last : String}
type alias Age = Int
personToString : Person -> String
personToString {first, last} = first ++ last
personDecode : Decode.Decoder Person
personDecode =
Decode.map2
Person
(Decode.field "first" Decode.string)
(Decode.field "last" Decode.string)
"[[{\"first\":\"Jeve\",\"last\":\"Sobs\"},9001],[{\"first\":\"Tim\",\"last\":\"Berners-Lee\"},1234]]"
|> Decode.decodeString (decodeList personToString (Decode.map2 Tuple.pair (Decode.index 0 personDecode) (Decode.index 1 Decode.int)))
--> Ok (fromList personToString [(Person "Jeve" "Sobs", 9001), (Person "Tim" "Berners-Lee", 1234)])Turn an AnyDict into a JSON object.
import Json.Encode
type Key = Foo | Bar
toString : Key -> String
toString key =
case key of
Foo -> "foo"
Bar -> "bar"
type alias Data = AnyDict String Key Int
encodeData : Data -> Json.Encode.Value
encodeData =
encode toString Json.Encode.int
fromList toString [(Foo, 1), (Bar, 2)]
|> encodeData
|> Json.Encode.encode 0
--> "{\"bar\":2,\"foo\":1}"Turn an AnyDict into a JSON list of tuples. This is useful when you have more complex types as keys
import Json.Decode as Decode
import Json.Encode as Encode
type alias Person = {first : String, last : String}
type alias Age = Int
personToString : Person -> String
personToString {first, last} = first ++ last
personEncode : Person -> Encode.Value
personEncode {first, last} =
Encode.object [("first", (Encode.string first)), ("last", (Encode.string last))]
example : AnyDict String Person Age
example =
fromList personToString [(Person "Jeve" "Sobs", 9001), (Person "Tim" "Berners-Lee", 1234)]
encodeList (\k v -> Encode.list identity [ personEncode k, Encode.int v ]) example
|> Encode.encode 0
--> "[[{\"first\":\"Jeve\",\"last\":\"Sobs\"},9001],[{\"first\":\"Tim\",\"last\":\"Berners-Lee\"},1234]]"
Be aware that AnyDict stores a function internally.
Use
equalfunction to check equality of twoAnyDicts. Using(==)would result in runtime exception becauseAnyDicttype contains a function.