elmcraft/core-extra/2.3.0

Dict.Extra

Convenience functions for working with Dict

List operations

groupBy : (a -> comparable) -> List a -> Dict comparable (List a)

Takes a key-fn and a list. Creates a Dict which maps the key to a list of matching elements.

import Dict


groupBy String.length [ "tree" , "apple" , "leaf" ]
--> Dict.fromList [ ( 4, [ "tree", "leaf" ] ), ( 5, [ "apple" ] ) ]

See also: List.Extra.gatherEqualsBy.

filterGroupBy : (a -> Maybe comparable) -> List a -> Dict comparable (List a)

Takes a key-fn and a list. Creates a Dict which maps the key to a list of matching elements, skipping elements where key-fn returns Nothing

import Dict


filterGroupBy (\v -> v |> String.uncons |> Maybe.map Tuple.first) [ "tree" , "", "tweet", "apple" , "leaf", "" ]
--> Dict.fromList [ ( 't', [ "tree", "tweet" ] ), ( 'a', [ "apple" ] ), ( 'l', [ "leaf" ] ) ]


filterGroupBy
    .car
    [ { name = "Mary"
      , car = Just "Ford"
      }
    , { name = "Jack"
      , car = Nothing
      }
    , { name = "Jill"
      , car = Just "Tesla"
      }
    , { name = "John"
      , car = Just "Tesla"
      }
    ]
--> Dict.fromList
--> [ ( "Ford"
-->   , [ { name = "Mary" , car = Just "Ford" } ]
-->   )
--> , ( "Tesla"
-->   , [ { name = "Jill" , car = Just "Tesla" }
-->     , { name = "John" , car = Just "Tesla" }
-->     ]
-->   )
--> ]
fromListBy : (a -> comparable) -> List a -> Dict comparable a

Create a dictionary from a list of values, by passing a function that can get a key from any such value. If the function does not return unique keys, earlier values are discarded.

import Dict


fromListBy String.length [ "tree" , "apple" , "leaf" ]
--> Dict.fromList [ ( 4, "leaf" ), ( 5, "apple" ) ]
fromListCombining : (a -> a -> a) -> List ( comparable, a ) -> Dict comparable a

Like Dict.fromList, but you provide a way to deal with duplicate keys. Create a dictionary from a list of pairs of keys and values, providing a function that is used to combine multiple values paired with the same key.

import Dict


fromListCombining
    (\a b -> a ++ " " ++ b)
    [ ( "class", "menu" ), ( "width", "100%" ), ( "class", "big" ) ]
--> Dict.fromList [ ( "class", "menu big" ), ( "width", "100%" ) ]
fromListByCombining : (a -> a -> a) -> (a -> comparable) -> List a -> Dict comparable a

fromListBy and fromListCombining rolled into one.

import Dict


fromListByCombining (\first second -> first) String.length [ "tree" , "apple" , "leaf" ]
--> Dict.fromList [ ( 4, "tree" ), ( 5, "apple" ) ]
frequencies : List comparable -> Dict comparable Int

Count the number of occurrences for each of the elements in the list.

import Dict


frequencies [ "A", "B", "C", "B", "C", "B" ]
--> Dict.fromList [ ( "A", 1 ), ( "B", 3 ), ( "C", 2 ) ]

Manipulation

removeWhen : (comparable -> v -> Bool) -> Dict comparable v -> Dict comparable v

Remove elements which satisfies the predicate.

import Dict


Dict.fromList [ ( "Mary", 1 ), ( "Jack", 2 ), ( "Jill", 1 ) ]
    |> removeWhen (\_ value -> value == 1 )
--> Dict.fromList [ ( "Jack", 2 ) ]
removeMany : Set comparable -> Dict comparable v -> Dict comparable v

Remove a key-value pair if its key appears in the set.

import Dict
import Set


Dict.fromList [ ( "Mary", 1 ), ( "Jack", 2 ), ( "Jill", 1 ) ]
    |> removeMany (Set.fromList [ "Mary", "Jill" ])
--> Dict.fromList [ ( "Jack", 2 ) ]
keepOnly : Set comparable -> Dict comparable v -> Dict comparable v

Keep a key-value pair if its key appears in the set.

import Dict
import Set


Dict.fromList [ ( "Mary", 1 ), ( "Jack", 2 ), ( "Jill", 1 ) ]
    |> keepOnly (Set.fromList [ "Jack", "Jill" ])
--> Dict.fromList [ ( "Jack", 2 ), ( "Jill", 1 ) ]
insertCombining : (v -> v -> v) -> comparable -> v -> Dict comparable v -> Dict comparable v

Insert an element at the given key, providing a combining function that used in the case that there is already an element at that key. The combining function is called with original element and the new element as arguments and returns the element to be inserted.

import Dict


Dict.fromList [ ( "expenses", 38.25 ), ( "assets", 100.85 ) ]
    |> insertCombining (+) "expenses" 2.50
    |> insertCombining (+) "liabilities" -2.50
--> Dict.fromList [ ( "expenses", 40.75 ), ( "assets", 100.85 ), ( "liabilities", -2.50 ) ]
updateIfExists : comparable -> (a -> a) -> Dict comparable a -> Dict comparable a

Updates a value if the key is present in the dictionary, leaves the dictionary untouched otherwise.

import Dict


Dict.fromList [ ( "expenses", 38.25 ), ( "assets", 100.85 ) ]
    |> updateIfExists "expenses" (\amount -> amount + 2.50)
    |> updateIfExists "liabilities" (\amount -> amount - 2.50)
    --> Dict.fromList [ ( "expenses", 40.75 ), ( "assets", 100.85 ) ]
upsert : comparable -> a -> (a -> a) -> Dict comparable a -> Dict comparable a

Updates a value if the key is present in the dictionary, inserts a new key-value pair otherwise.

import Dict


Dict.fromList [ ( "expenses", 38.25 ), ( "assets", 100.85 ) ]
    |> upsert "expenses" 4.50 (\amount -> amount + 2.50)
    |> upsert "liabilities" 2.50 (\amount -> amount - 2.50)
    --> Dict.fromList [ ( "expenses", 40.75 ), ( "assets", 100.85 ), ( "liabilities", 2.50 ) ]
invert : Dict comparable1 comparable2 -> Dict comparable2 comparable1

Inverts the keys and values of an array.

import Dict


Dict.fromList [ ("key", "value")  ]
    |> invert
--> Dict.fromList [ ( "value", "key" ) ]
Dict comparable1 comparable2
-> Dict comparable2 (Set comparable1)

Like invert, it changes the keys and values. However, if one value maps to multiple keys, then all of the keys will be retained.

import Dict
import Set


Dict.fromList [ ( 1, "Jill" ), ( 2, "Jill" ), ( 3, "Jack" ) ]
    |> invertAll
--> Dict.fromList [ ( "Jill", Set.fromList [ 1, 2 ] ), ( "Jack", Set.singleton 3 ) ]

Maps

(comparable -> comparable1)
-> Dict comparable v
-> Dict comparable1 v

Apply a function to all keys in a dictionary.

import Dict


Dict.fromList [ ( 5, "Jack" ), ( 10, "Jill" ) ]
    |> mapKeys (\x -> x + 1)
--> Dict.fromList [ ( 6, "Jack" ), ( 11, "Jill" ) ]


Dict.fromList [ ( 5, "Jack" ), ( 10, "Jill" ) ]
    |> mapKeys String.fromInt
--> Dict.fromList [ ( "5", "Jack" ), ( "10", "Jill" ) ]
filterMap : (comparable -> a -> Maybe b) -> Dict comparable a -> Dict comparable b

Apply a function that may or may not succeed to all entries in a dictionary, but only keep the successes.

import Dict


let
    isTeen n a =
        if 13 <= n && n <= 19 then
            Just <| String.toUpper a
        else
            Nothing
in
Dict.fromList [ ( 5, "Jack" ), ( 15, "Jill" ), ( 20, "Jones" ) ]
    |> filterMap isTeen
--> Dict.fromList [ ( 15, "JILL" ) ]

Predicates

any : (comparable -> a -> Bool) -> Dict comparable a -> Bool

Determine if any key/value pair satisfies some test.

import Dict


Dict.fromList [ ( 9, "Jill" ), ( 7, "Jill" ) ]
    |> any (\_ value -> value == "Jill")
--> True


Dict.fromList [ ( 9, "Jill" ), ( 7, "Jill" ) ]
    |> any (\key _ -> key == 5)
--> False
all : (comparable -> a -> Bool) -> Dict comparable a -> Bool

Determine if all key/value pairs satisfies some test.

import Dict


Dict.fromList [ ( 9, "Jill" ), ( 7, "Jill" ) ]
    |> all (\_ value -> value == "Jill")
--> True


Dict.fromList [ ( 9, "Jill" ), ( 7, "Jill" ) ]
    |> all (\key _ -> key == 9)
--> False

Search

(comparable -> a -> Bool)
-> Dict comparable a
-> Maybe ( comparable, a )

Find the first key/value pair that matches a predicate.

import Dict


Dict.fromList [ ( 9, "Jill" ), ( 7, "Jill" ) ]
    |> find (\_ value -> value == "Jill")
--> Just ( 7, "Jill" )


Dict.fromList [ ( 9, "Jill" ), ( 7, "Jill" ) ]
    |> find (\key _ -> key == 5)
--> Nothing

Combine

(comparable -> a -> a -> a)
-> Dict comparable a
-> Dict comparable a
-> Dict comparable a

Combine two dictionaries. If there is a collision, a combining function is used to combine the two values.

import Dict


unionWith (\k v1 v2 -> String.fromInt k ++ v1 ++ v2 )
    (Dict.fromList [ ( 1, "123" ), ( 2, "abc" ) ])
    (Dict.fromList [ ( 2, "def" ), ( 3, "xyz" ) ])
    --> Dict.fromList [ ( 1, "123" ), ( 2, "2abcdef" ), ( 3, "xyz" ) ]

Note that, like Dict.union, it is more efficient to have the larger Dict as the second argument, i.e. when possible, you should use unionWith f new old, if old has more keys than new.