Header menu logo Dicts

Logo

Dicts

Dicts on nuget.org Build Status Docs Build Status Test Status license code size

This F# library provides:

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

goswinr.github.io/Dicts

Tests

All Tests run in both javascript and dotnet. Successful Fable compilation to typescript is verified too. Go to the tests folder:

cd Tests

For testing with .NET using Expecto:

dotnet run

for JS testing with Fable.Mocha and TS verification:

npm test

License

MIT

Changelog

see CHANGELOG.md

namespace Dicts
val d: Dict<string,int>
Multiple items
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&lt;'K,'V&gt; with nicer Error messages on accessing missing keys. </summary>

--------------------
new: unit -> Dict<'K,'V>
new: iEqualityComparer: System.Collections.Generic.IEqualityComparer<'K> -> Dict<'K,'V>
Multiple items
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>
member Dict.Get: key: 'K -> 'V
member Dict.Set: key: 'K -> value: 'V -> unit
property Dict.Count: int with get
<summary> Gets the number of key/value pairs contained in the Dict </summary>
member Dict.ContainsKey: key: 'K -> bool
member Dict.DoesNotContainKey: key: 'K -> bool
property Dict.IsEmpty: bool with get
<summary> Tests if the Dict is Empty. </summary>
property Dict.IsNotEmpty: bool with get
<summary> Tests if the Dict is NOT Empty. </summary>
member Dict.Pop: key: 'K -> 'V
member Dict.TryPop: key: 'K -> 'V option
member Dict.SetIfKeyAbsent: key: 'K -> value: 'V -> bool
member Dict.GetOrSetDefaultValue: defaultValue: 'V -> key: 'K -> 'V
member Dict.GetOrSetDefault: getDefault: ('K -> 'V) -> key: 'K -> 'V
val k: string
property System.String.Length: int with get
val counter: DefaultDict<string,int ref>
Multiple items
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&lt;'K,'V&gt; 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&lt;'K,'V&gt; type and it's method &lt;c&gt;Dicts.getOrSetDefault func key&lt;/c&gt;. </summary>

--------------------
new: defaultOfKeyFun: ('K -> 'V) -> DefaultDict<'K,'V>
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
Multiple items
val ref: value: 'T -> 'T ref

--------------------
type 'T ref = Ref<'T>
val word: string
val incr: cell: int ref -> unit
val groups: DefaultDict<string,ResizeArray<int>>
type ResizeArray<'T> = System.Collections.Generic.List<'T>
module Seq from Microsoft.FSharp.Collections
val toList: source: 'T seq -> 'T list
val dd: DefaultDict<string,int>
member DefaultDict.ContainsKey: k: 'K -> bool
val ok: bool
member DefaultDict.TryGetValue: k: 'K -> bool * 'V
Multiple items
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
Multiple items
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]&lt;-v </summary>

--------------------
static member Dict.set: key: 'K -> value: 'V -> dd: Dict<'K,'V> -> unit
val tryGet: k: 'Key -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Value option
<summary> Tries to get a value from a IDictionary </summary>
val pop: key: 'Key -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Value
<summary> Get a value and remove key and value it from dictionary, like *.pop() in Python. Will fail if key does not exist </summary>
val tryPop: key: 'Key -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Value option
<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>
val setIfKeyAbsent: key: 'Key -> value: 'Value -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> bool
<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 &lt;c&gt;Dict.addIfKeyAbsent key value dic&lt;/c&gt; </summary>
val getOrSetDefaultValue: defaultValue: 'Value -> key: 'Key -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Value
<summary> If the key ist not present set it as value at the key and return the value. </summary>
val keys: dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Key seq
<summary> Returns a (lazy) sequence of Keys </summary>
val values: dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'Value seq
<summary> Returns a (lazy) sequence of values </summary>
val items: dic: System.Collections.Generic.IDictionary<'Key,'Value> -> ('Key * 'Value) seq
<summary> Returns a (lazy) sequence of key and value tuples </summary>
val iter: f: ('Key -> 'Value -> unit) -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> unit
<summary> Iterate over keys and values of a Dict </summary>
val v: int
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val map: f: ('Key -> 'Value -> 'T) -> dic: System.Collections.Generic.IDictionary<'Key,'Value> -> 'T seq
<summary> Map over keys and values of a Dict </summary>
val expensiveComputation: (int -> int)
val memoize: f: ('T -> 'U) -> ('T -> 'U) (requires equality)
<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>
val n: int
module ExtensionsIDictionary from Dicts
<summary> Provides Extensions for IDictionary&lt;'K,'V&gt; interface. Such as Items as key value tuples , Pop(key) or GetValue with nicer error message) </summary>
val d: System.Collections.Generic.Dictionary<string,int>
namespace System
namespace System.Collections
namespace System.Collections.Generic
Multiple items
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>
member System.Collections.Generic.IDictionary.GetValue: k: 'K -> 'V
member System.Collections.Generic.IDictionary.SetValue: k: 'K -> v: 'V -> unit
member System.Collections.Generic.IDictionary.Pop: k: 'K -> 'V
member System.Collections.Generic.IDictionary.TryPop: k: 'K -> 'V option
property System.Collections.Generic.IDictionary.Items: ('K * 'V) seq with get
<summary> Returns a lazy seq of key and value tuples </summary>
property System.Collections.Generic.IDictionary.KeysSeq: 'K seq with get
<summary> Returns a (lazy) sequence of Keys </summary>
property System.Collections.Generic.IDictionary.ValuesSeq: 'V seq with get
<summary> Returns a (lazy) sequence of values </summary>
member System.Collections.Generic.IDictionary.DoesNotContainKey: key: 'K -> bool
val d: Dict<string,string>
override Dict.ToString: unit -> string
member System.Collections.Generic.IDictionary.ToString: entriesToPrint: int -> string
member Dict.ToString: entriesToPrint: int -> string
Multiple items
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>

Type something to start searching.