
Dicts
This F# library provides:
A dedicated
Dict<'T>type. It is a thin wrapper aroundDictionary<'T>with more functionality and nicer Error messages.-
A
DefaultDict<'T>type. It works like Python's' defaultdict.
By providing a default function in the constructor it will always return a value for any key. Extension methods for working with the
IDictionary<'T>interface.
It also works in JS and TS with Fable.
This library was designed for use with F# scripting.
Functions and methods never return null.
Only functions starting with try... will return an F# Option.
Otherwise when a function fails on invalid input it will throw a descriptive exception.
I was always annoyed that a KeyNotFoundExceptions does not include the actual bad key nor a pretty printed dictionary.
This library fixes that in iDictionary.Get, iDictionary.Set and other item access functions.
Examples
All examples assume:
#r "nuget: Dicts"
open Dicts
Dict — Better error messages
Dict<'K,'V> is a thin wrapper around Dictionary<'K,'V> that gives descriptive exceptions when a key is missing, including the key, item count, and a pretty-printed dictionary.
// Create from key-value pairs
let d = Dict.create [ "a", 1; "b", 2; "c", 3 ]
d.["a"] // 1
d.Get "b" // 2
d.Set "d" 4 // adds or updates key "d"
d.Count // 4
// Nicer error messages than System.Collections.Generic.Dictionary:
d.["z"] // throws KeyNotFoundException:
// "Dict.get failed to find key "z" in Dict<...> of 4 items"
// Check for keys
d.ContainsKey "a" // true
d.DoesNotContainKey "z" // true
// Use IsEmpty / IsNotEmpty
d.IsEmpty // false
d.IsNotEmpty // true
Dict — Pop and TryPop (Python-like)
let d = Dict.create [ "x", 10; "y", 20 ]
d.Pop "x" // 10 (key "x" is removed from d)
d.Count // 1
d.TryPop "y" // Some 20 (key "y" is removed)
d.TryPop "y" // None (already removed, no exception)
Dict — Conditional set and defaults
let d = Dict.create [ "a", 1 ]
// Only sets if key is absent, returns true if it was set
d.SetIfKeyAbsent "a" 99 // false (key "a" exists, value stays 1)
d.SetIfKeyAbsent "b" 42 // true (key "b" is now 42)
// Get existing value, or create and store a default
d.GetOrSetDefaultValue 0 "c" // 0 (key "c" is now set to 0)
d.GetOrSetDefaultValue 0 "a" // 1 (key "a" already exists)
// Default from a function that receives the key
d.GetOrSetDefault (fun k -> k.Length) "hello" // 5
DefaultDict — Auto-creating missing keys
DefaultDict<'K,'V> calls a default function whenever a missing key is accessed.
This is inspired by Python's defaultdict.
// Count word occurrences using a mutable ref cell
let counter = DefaultDict<string, int ref>(fun _ -> ref 0)
for word in ["hi"; "world"; "hi"; "hi"] do
incr counter.[word]
counter.["hi"].Value // 3
counter.["world"].Value // 1
// Group items by key
let groups = DefaultDict<string, ResizeArray<int>>(fun _ -> ResizeArray())
groups.["evens"].Add 2
groups.["odds"].Add 1
groups.["evens"].Add 4
groups.["evens"] |> Seq.toList // [2; 4]
groups.["odds"] |> Seq.toList // [1]
Important: Accessing a missing key with Get or the indexer .[key] creates it.
Use TryGetValue or ContainsKey to check without creating:
let dd = DefaultDict<string, int>(fun _ -> 0)
dd.ContainsKey "x" // false (does not create "x")
let ok, _ = dd.TryGetValue "x" // ok = false (does not create "x")
dd.["x"] // 0 (now "x" IS created with the default)
dd.ContainsKey "x" // true
Dict module — Functional-style operations
The Dict module provides functions that work on any IDictionary<'K,'V>, including plain Dictionary and Dict.
let d = Dict.create [ "a", 1; "b", 2; "c", 3 ]
// Functional get / set / tryGet
Dict.get "a" d // 1
Dict.set "d" 4 d // sets key "d" to 4
Dict.tryGet "z" d // None
Dict.tryGet "a" d // Some 1
// Pop and tryPop
Dict.pop "d" d // 4 (removes key "d")
Dict.tryPop "d" d // None (already removed)
// Conditional set
Dict.setIfKeyAbsent "a" 99 d // false (key "a" exists)
Dict.setIfKeyAbsent "e" 5 d // true (key "e" is now 5)
// Get or create a default
Dict.getOrSetDefaultValue 0 "f" d // 0 (key "f" is now 0)
// Iteration
Dict.keys d |> Seq.toList // ["a"; "b"; "c"; "e"; "f"]
Dict.values d |> Seq.toList // [1; 2; 3; 5; 0]
Dict.items d |> Seq.toList // [("a",1); ("b",2); ("c",3); ("e",5); ("f",0)]
Dict.iter (fun k v -> printfn "%s = %d" k v) d
Dict.map (fun k v -> $"{k}:{v}") d |> Seq.toList
Dict.memoize — Cache function results
let expensiveComputation = Dict.memoize (fun n ->
printfn "computing %d..." n
n * n
)
expensiveComputation 5 // prints "computing 5...", returns 25
expensiveComputation 5 // returns 25 immediately, no print
IDictionary extensions
Extension methods available on any IDictionary<'K,'V> (including Dictionary<'K,'V>):
open Dicts.ExtensionsIDictionary
let d = System.Collections.Generic.Dictionary<string,int>()
d.["x"] <- 10; d.["y"] <- 20; d.["z"] <- 30
d.GetValue "x" // 10 (with descriptive error on missing key)
d.SetValue "w" 40 // adds key "w"
d.Pop "w" // 40 (removes key "w")
d.TryPop "w" // None
d.Items |> Seq.toList // seq of (key, value) tuples
d.KeysSeq |> Seq.toList // seq of keys
d.ValuesSeq |> Seq.toList // seq of values
d.DoesNotContainKey "w" // true
Pretty printing
All types provide readable string representations:
let d = Dict.create [ "name", "Alice"; "city", "Zurich" ]
d.ToString() // "Dict<String,String> with 2 items"
d.AsString // "Dict<String,String> with 2 items:\n name : Alice\n city : Zurich\n"
d.ToString(1) // prints only the first entry
Full API Documentation
Tests
All Tests run in both javascript and dotnet. Successful Fable compilation to typescript is verified too. Go to the tests folder:
|
For testing with .NET using Expecto:
|
for JS testing with Fable.Mocha and TS verification:
|
License
Changelog
see CHANGELOG.md
module Dict from Dicts
<summary> Static Functions on IDictionary Interface </summary>
--------------------
type Dict<'K,'V (requires equality)> = interface IReadOnlyDictionary<'K,'V> interface IReadOnlyCollection<KeyValuePair<'K,'V>> interface IDictionary<'K,'V> interface ICollection<KeyValuePair<'K,'V>> interface IEnumerable interface IEnumerable<KeyValuePair<'K,'V>> new: unit -> Dict<'K,'V> + 1 overload member Add: key: 'K * value: 'V -> unit member AddIfKeyAbsent: key: 'K -> value: 'V -> bool member Clear: unit -> unit ...
<summary> A thin wrapper over Collections.Generic.Dictionary<'K,'V> with nicer Error messages on accessing missing keys. </summary>
--------------------
new: unit -> Dict<'K,'V>
new: iEqualityComparer: System.Collections.Generic.IEqualityComparer<'K> -> Dict<'K,'V>
val create: xs: ('Key * 'Value) seq -> Dict<'Key,'Value> (requires equality)
<summary> Create a Dict from seq of key and value pairs </summary>
--------------------
static member Dict.create: pairs: ('K * 'V) seq -> Dict<'K,'V>
<summary> Gets the number of key/value pairs contained in the Dict </summary>
<summary> Tests if the Dict is Empty. </summary>
<summary> Tests if the Dict is NOT Empty. </summary>
type DefaultDict<'K,'V (requires equality)> = interface IReadOnlyCollection<KeyValuePair<'K,'V>> interface ICollection<KeyValuePair<'K,'V>> interface IEnumerable interface IEnumerable<KeyValuePair<'K,'V>> new: defaultOfKeyFun: ('K -> 'V) -> DefaultDict<'K,'V> member Add: k: 'K * v: 'V -> unit member Clear: unit -> unit member ContainsKey: k: 'K -> bool member ContainsValue: v: 'V -> bool member DoesNotContainKey: key: 'K -> bool ...
<summary> A Collections.Generic.Dictionary<'K,'V> with default Values that get created upon accessing a missing key. If accessing a non exiting key , the default function is called to create and set it. Inspired by the defaultdict in Python. If you need to provide a custom implementation of the default function depending on each key, then use the Dict<'K,'V> type and it's method <c>Dicts.getOrSetDefault func key</c>. </summary>
--------------------
new: defaultOfKeyFun: ('K -> 'V) -> DefaultDict<'K,'V>
val string: value: 'T -> string
--------------------
type string = System.String
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
val ref: value: 'T -> 'T ref
--------------------
type 'T ref = Ref<'T>
val get: key: 'Key -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Value
<summary> Get value at key from IDictionary, with nicer Error messages </summary>
--------------------
static member Dict.get: key: 'K -> dd: Dict<'K,'V> -> 'V
val set: key: 'Key -> value: 'Value -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> unit
<summary> Set value at key in a IDictionary just d.[k]<-v </summary>
--------------------
static member Dict.set: key: 'K -> value: 'V -> dd: Dict<'K,'V> -> unit
<summary> Tries to get a value from a IDictionary </summary>
<summary> Get a value and remove key and value it from dictionary, like *.pop() in Python. Will fail if key does not exist </summary>
<summary> Tries to get a value and remove key and value it from dictionary, like *.pop() in Python. Will return None if key does not exist </summary>
<summary> Set value only if key does not exist yet. Returns false if key already exist, does not set value in this case Same as <c>Dict.addIfKeyAbsent key value dic</c> </summary>
<summary> If the key ist not present set it as value at the key and return the value. </summary>
<summary> Returns a (lazy) sequence of Keys </summary>
<summary> Returns a (lazy) sequence of values </summary>
<summary> Returns a (lazy) sequence of key and value tuples </summary>
<summary> Iterate over keys and values of a Dict </summary>
<summary> Map over keys and values of a Dict </summary>
<summary> Caches the results of a function in a Dictionary. The argument 'T is packed in a wrapper so it can be unit or null(=None) too. (A Dictionary would fail on a null as key ) </summary>
<summary> Provides Extensions for IDictionary<'K,'V> interface. Such as Items as key value tuples , Pop(key) or GetValue with nicer error message) </summary>
type Dictionary<'TKey,'TValue> = interface ICollection<KeyValuePair<'TKey,'TValue>> interface IEnumerable<KeyValuePair<'TKey,'TValue>> interface IEnumerable interface IDictionary<'TKey,'TValue> interface IReadOnlyCollection<KeyValuePair<'TKey,'TValue>> interface IReadOnlyDictionary<'TKey,'TValue> interface ICollection interface IDictionary interface IDeserializationCallback interface ISerializable ...
<summary>Represents a collection of keys and values.</summary>
<typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
<typeparam name="TValue">The type of the values in the dictionary.</typeparam>
--------------------
System.Collections.Generic.Dictionary() : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(dictionary: System.Collections.Generic.IDictionary<'TKey,'TValue>) : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(collection: System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<'TKey,'TValue>>) : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(capacity: int) : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(dictionary: System.Collections.Generic.IDictionary<'TKey,'TValue>, comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(collection: System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<'TKey,'TValue>>, comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : System.Collections.Generic.Dictionary<'TKey,'TValue>
System.Collections.Generic.Dictionary(capacity: int, comparer: System.Collections.Generic.IEqualityComparer<'TKey>) : System.Collections.Generic.Dictionary<'TKey,'TValue>
<summary> Returns a lazy seq of key and value tuples </summary>
<summary> Returns a (lazy) sequence of Keys </summary>
<summary> Returns a (lazy) sequence of values </summary>
member System.Collections.Generic.IDictionary.ToString: entriesToPrint: int -> string
member Dict.ToString: entriesToPrint: int -> string
property Dict.AsString: string with get
<summary> A string representation of the Dict including the count of entries and the first 5 entries. When used in Fable this member is inlined for reflection to work. </summary>
--------------------
property System.Collections.Generic.IDictionary.AsString: string with get
<summary> A string representation of the IDictionary including the count of entries and the first 5 entries. </summary>
Dicts