Janiczek / elm-bidict / MultiDict.Assoc

A dictionary mapping unique keys to multiple values, allowing for modelling one-to-many relationships.

Example usage:

oneToMany : MultiDict String Int
oneToMany =
    MultiDict.empty
        |> MultiDict.insert "A" 1
        |> MultiDict.insert "B" 2
        |> MultiDict.insert "C" 3
        |> MultiDict.insert "A" 2

MultiDict.get "A" oneToMany
--> Set.fromList [1, 2]

This module in particular uses assoc-list and assoc-set under the hood to get rid of the comparable constraint on keys that's usually associated with Dicts and Sets.

Dictionaries


type MultiDict a b

The underlying data structure. Think about it as

 type alias MultiDict a b =
     Dict a (Set b) -- just a normal Dict!

Differences from Dict

toDict : MultiDict a b -> AssocList.Dict a (AssocSet.Set b)

Convert MultiDict into a Dict. (Throw away the reverse mapping.)

fromDict : AssocList.Dict a (AssocSet.Set b) -> MultiDict a b

Convert Dict into a MultiDict. (Compute the reverse mapping.)

Build

empty : MultiDict a b

Create an empty dictionary.

singleton : a -> b -> MultiDict a b

Create a dictionary with one key-value pair.

insert : a -> b -> MultiDict a b -> MultiDict a b

Insert a key-value pair into a dictionary. Replaces value when there is a collision.

update : a -> (AssocSet.Set b -> AssocSet.Set b) -> MultiDict a b -> MultiDict a b

Update the value of a dictionary for a specific key with a given function.

remove : a -> b -> MultiDict a b -> MultiDict a b

Remove a single key-value pair from a dictionary. If the key is not found, no changes are made.

removeAll : a -> MultiDict a b -> MultiDict a b

Remove all key-value pairs for the given key from a dictionary. If the key is not found, no changes are made.

Query

isEmpty : MultiDict a b -> Basics.Bool

Determine if a dictionary is empty.

isEmpty empty == True

member : a -> MultiDict a b -> Basics.Bool

Determine if a key is in a dictionary.

get : a -> MultiDict a b -> AssocSet.Set b

Get 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.

animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]

get "Tom"   animals == Just Cat
get "Jerry" animals == Just Mouse
get "Spike" animals == Nothing

size : MultiDict a b -> Basics.Int

Determine the number of key-value pairs in the dictionary.

Lists

keys : MultiDict a b -> List a

Get all of the keys in a dictionary, sorted from lowest to highest.

keys (fromList [ ( 0, "Alice" ), ( 1, "Bob" ) ]) == [ 0, 1 ]

values : MultiDict a b -> List b

Get all of the values in a dictionary, in the order of their keys.

values (fromList [ ( 0, "Alice" ), ( 1, "Bob" ) ]) == [ "Alice", "Bob" ]

toList : MultiDict a b -> List ( a, AssocSet.Set b )

Convert a dictionary into an association list of key-value pairs, sorted by keys.

fromList : List ( a, AssocSet.Set b ) -> MultiDict a b

Convert an association list into a dictionary.

fromFlatList : List ( a, b ) -> MultiDict a b

Convert an association list into a dictionary.

fromFlatList
    [ ( "foo", 1 )
    , ( "bar", 2 )
    , ( "foo", 3 )
    ]

results in the same dict as

fromList
    [ ( "foo", Set.fromList [ 1, 3 ] )
    , ( "bar", Set.fromList [ 2 ] )
    ]

Transform

map : (a -> b1 -> b2) -> MultiDict a b1 -> MultiDict a b2

Apply a function to all values in a dictionary.

foldl : (a -> AssocSet.Set b -> acc -> acc) -> acc -> MultiDict a b -> acc

Fold over the key-value pairs in a dictionary from lowest key to highest key.

getAges users =
    Dict.foldl addAge [] users

addAge _ user ages =
    user.age :: ages

-- getAges users == [33,19,28]

foldr : (a -> AssocSet.Set b -> acc -> acc) -> acc -> MultiDict a b -> acc

Fold over the key-value pairs in a dictionary from highest key to lowest key.

getAges users =
    Dict.foldr addAge [] users

addAge _ user ages =
    user.age :: ages

-- getAges users == [28,19,33]

filter : (a -> b -> Basics.Bool) -> MultiDict a b -> MultiDict a b

Keep only the mappings that pass the given test.

partition : (a -> AssocSet.Set b -> Basics.Bool) -> MultiDict a b -> ( MultiDict a b, MultiDict a b )

Partition a dictionary according to some test. The first dictionary contains all key-value pairs which passed the test, and the second contains the pairs that did not.

Combine

union : MultiDict a b -> MultiDict a b -> MultiDict a b

Combine two dictionaries. If there is a collision, preference is given to the first dictionary.

intersect : MultiDict a b -> MultiDict a b -> MultiDict a b

Keep a key-value pair when its key appears in the second dictionary. Preference is given to values in the first dictionary.

diff : MultiDict a b -> MultiDict a b -> MultiDict a b

Keep a key-value pair when its key does not appear in the second dictionary.

merge : (a -> AssocSet.Set b1 -> acc -> acc) -> (a -> AssocSet.Set b1 -> AssocSet.Set b2 -> acc -> acc) -> (a -> AssocSet.Set b2 -> acc -> acc) -> MultiDict a b1 -> MultiDict a b2 -> acc -> acc

The most general way of combining two dictionaries. You provide three accumulators for when a given key appears:

  1. Only in the left dictionary.
  2. In both dictionaries.
  3. Only in the right dictionary.

You then traverse all the keys from lowest to highest, building up whatever you want.