turboMaCk / any-set / Set.Any

A set of unique values. Similar to elm/core Set but allows arbitrary data given a function for converting to comparable can be provided.

Insert, remove, and query operations all take O(log n) time.

Converting Types to Comparable

When writing a function for conversion from the type you want to use within a set to comparable it's very important to make sure every distinct member of the type produces different value in the set of comparables. Functions satisfying this property are called injective.

Take for instance those two examples:

We can use store Bool in Set (No matter how unpractical it might seem)

boolToInt : Bool -> Int
boolToInt bool =
    case bool of
        False -> 0
        True -> 1

empty boolToInt
|> insert True
|> member True
--> True

or Maybe String.

comparableKey : Maybe String -> (Int, String)
comparableKey maybe =
    case maybe of
        Nothing -> (0, "")
        Just str -> (1, str)

empty comparableKey
    |> insert (Just "foo")
    |> member (Just "foo")
--> True

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

AnySet


type AnySet comparable t
    = AnySet (Dict.Any.AnyDict comparable t ())

Represents a set of unique values.

Be aware that AnySet stores a function internally. This means it's not possible to use (==) for comparing two AnySets. Use equal function instead.

equal : AnySet comparable t -> AnySet comparable t -> Basics.Bool

Check equality of two AnySets

Build

empty : (a -> comparable) -> AnySet comparable a

Create an empty set.

** Note that it's important to make sure every key is turned to different comparable.** Otherwise keys would conflict and overwrite each other.

singleton : a -> (a -> comparable) -> AnySet comparable a

Create a set with one value.

** 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 -> AnySet comparable a -> AnySet comparable a

Insert a value into a set.

remove : a -> AnySet comparable a -> AnySet comparable a

Remove a value from a set. If the value is not found, no changes are made.

removeAll : AnySet comparable a -> AnySet comparable a

Remove all entries from AnySet.

Useful when you need to create new empty AnySet using same comparable function for key type.

toggle : a -> AnySet comparable a -> AnySet comparable a

Toggle a value in a set. If it's not found, it's inserted, else it's removed.

Query

isEmpty : AnySet comparable a -> Basics.Bool

Determine if a set is empty.

member : a -> AnySet comparable a -> Basics.Bool

Determine if a value is in a set.

get : a -> AnySet comparable a -> Maybe a

Get a value from set.

This is useful in case of AnySet because some parts of a value might not be used for generating comparable. This function allows quering AnySet with old value to obtain updated one in such cases.

size : AnySet comparable a -> Basics.Int

Determine the number of elements in a set.

any : (v -> Basics.Bool) -> AnySet comparable v -> Basics.Bool

Find out if there is any instance of something in a set.

type Animal = Cat | Mouse | Dog

animalToInt : Animal -> Int
animalToInt animal =
    case animal of
        Cat -> 0
        Mouse -> 1
        Dog -> 2

animals : AnySet Int Animal
animals =
    [ Cat, Mouse ]
        |> fromList animalToInt

isACat : Animal -> Bool
isACat animal =
    case animal of
        Cat -> True
        _ -> False

any isACat animals
--> True

all : (v -> Basics.Bool) -> AnySet comparable v -> Basics.Bool

Find out if all values in set match a predicate.

type Animal = Cat | Mouse | Dog

animalToInt : Animal -> Int
animalToInt animal =
    case animal of
        Cat -> 0
        Mouse -> 1
        Dog -> 2

animals : AnySet Int Animal
animals =
    [ Cat, Mouse ]
        |> fromList animalToInt

aristocats : AnySet Int Animal
aristocats =
    [ Cat ]
        |> fromList animalToInt

isACat : Animal -> Bool
isACat animal =
    case animal of
        Cat -> True
        _ -> False

all isACat animals
--> False

all isACat aristocats
--> True

Combine

union : AnySet comparable a -> AnySet comparable a -> AnySet comparable a

Get the union of two sets. Keep all values.

intersect : AnySet comparable a -> AnySet comparable a -> AnySet comparable a

Get the intersection of two sets. Keeps values that appear in both sets.

diff : AnySet comparable a -> AnySet comparable a -> AnySet comparable a

Get the difference between the first set and the second. Keeps values that do not appear in the second set.

symDifference : AnySet comparable a -> AnySet comparable a -> AnySet comparable a

In Math, the symmetric difference of two sets, also known as disjunctive union is the set of elements which are in either of the sets, but not in their intersection. For example, the symmetric difference of the sets {1,2,3} and {3,4} is {1,2,4}.

See https://en.m.wikipedia.org/wiki/Symmetric_difference for more reference.

type Animal = Cat | Mouse | Dog

animalToInt : Animal -> Int
animalToInt animal =
    case animal of
        Cat -> 0
        Mouse -> 1
        Dog -> 2

animals : AnySet Int Animal
animals =
    [ Cat, Mouse ]
        |> fromList animalToInt

moreAnimals : AnySet Int Animal
moreAnimals =
    [ Mouse, Dog ]
        |> fromList animalToInt

diffAnimals : AnySet Int Animal
diffAnimals =
    [ Cat, Mouse, Dog ]
        |> fromList animalToInt

symDifference animals moreAnimals == diffAnimals
--> True

Lists

toList : AnySet comparable a -> List a

Convert a set into a list, sorted from lowest to highest.

fromList : (a -> comparable) -> List a -> AnySet comparable a

Convert a list into a set, removing any duplicates.

Transform

map : (b -> comparable2) -> (a -> b) -> AnySet comparable a -> AnySet comparable2 b

Map a function onto a set, creating a new set with no duplicates.

foldl : (a -> b -> b) -> b -> AnySet comparable a -> b

Fold over the values in a set, in order from highest to lowest.

foldr : (a -> b -> b) -> b -> AnySet comparable a -> b

Fold over the values in a set, in order from lowest to highest.

filter : (a -> Basics.Bool) -> AnySet comparable a -> AnySet comparable a

Only keep elements that pass the given test.

partition : (a -> Basics.Bool) -> AnySet comparable a -> ( AnySet comparable a, AnySet comparable a )

Create two new sets. The first contains all the elements that passed the given test, and the second contains all the elements that did not.

filterMap : (b -> comparable) -> (a -> Maybe b) -> AnySet comparable2 a -> AnySet comparable b

Apply a function that may or may not succeed to all entries in a set, 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 : AnySet Int Animal
animals =
    [ Cat, Mouse ]
        |> fromList animalToInt

onlyTom : AnySet String String
onlyTom =
    [ "Tom" ]
        |> fromList identity

getCatName : Animal -> Maybe String
getCatName animal =
    case animal of
        Cat -> Just "Tom"
        _ -> Nothing

filterMap identity getCatName animals == onlyTom
--> True

Set

toSet : AnySet comparable a -> Set comparable

Convert AnySet to elm/core Set of comparable

Json

decode : (a -> comparable) -> Json.Decode.Decoder a -> Json.Decode.Decoder (AnySet comparable a)

Decode AnySet from JSON array.

encode : (a -> Json.Encode.Value) -> AnySet comparable a -> Json.Encode.Value

Turn AnySet into JSON array.