Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/FSharpx.Collections/PersistentHashMap.fs
Original file line number Diff line number Diff line change
Expand Up @@ -929,4 +929,75 @@ module PersistentHashMap =

ret.persistent()

///O(log32n), returns the value option for the given key.
let tryFind key (map: PersistentHashMap<'T, 'S>) =
if map.ContainsKey key then Some map.[key] else None

///O(n). Returns a new HashMap containing only the entries for which the predicate returns true.
let filter (predicate: 'T -> 'S -> bool) (map: PersistentHashMap<'T, 'S>) : PersistentHashMap<'T, 'S> =
let mutable ret = TransientHashMap<'T, 'S>.Empty()

for (key, value) in map do
if predicate key value then
ret <- ret.Add(key, value)

ret.persistent()

///O(n). Applies the supplied function to each element of the HashMap.
let iter (action: 'T -> 'S -> unit) (map: PersistentHashMap<'T, 'S>) =
for (key, value) in map do
action key value

///O(n). Applies a function to each entry of the HashMap, threading an accumulator argument.
let fold (folder: 'State -> 'T -> 'S -> 'State) (state: 'State) (map: PersistentHashMap<'T, 'S>) =
let mutable acc = state

for (key, value) in map do
acc <- folder acc key value

acc

///O(n). Returns true if any entry satisfies the predicate.
let exists (predicate: 'T -> 'S -> bool) (map: PersistentHashMap<'T, 'S>) =
map |> Seq.exists(fun (k, v) -> predicate k v)

///O(n). Returns true if all entries satisfy the predicate.
let forall (predicate: 'T -> 'S -> bool) (map: PersistentHashMap<'T, 'S>) =
map |> Seq.forall(fun (k, v) -> predicate k v)

///O(n). Builds a new HashMap whose entries are the results of applying the given function to each entry. Entries for which the function returns None are excluded.
let choose (chooser: 'T -> 'S -> 'S1 option) (map: PersistentHashMap<'T, 'S>) : PersistentHashMap<'T, 'S1> =
let mutable ret = TransientHashMap<'T, 'S1>.Empty()

for (key, value) in map do
match chooser key value with
| Some v -> ret <- ret.Add(key, v)
| None -> ()

ret.persistent()

///O(n). Returns a list of all key-value pairs in the HashMap.
let toList(map: PersistentHashMap<'T, 'S>) =
[ for kv in map -> kv ]

///O(n). Returns an array of all key-value pairs in the HashMap.
let toArray(map: PersistentHashMap<'T, 'S>) =
[| for kv in map -> kv |]

///O(n). Creates a HashMap from a list of key-value pairs.
let ofList(items: ('T * 'S) list) =
PersistentHashMap<'T, 'S>.ofSeq items

///O(n). Creates a HashMap from an array of key-value pairs.
let ofArray(items: ('T * 'S) array) =
PersistentHashMap<'T, 'S>.ofSeq items

///O(n). Returns a sequence of all keys in the HashMap.
let keys(map: PersistentHashMap<'T, 'S>) =
map |> Seq.map fst

///O(n). Returns a sequence of all values in the HashMap.
let values(map: PersistentHashMap<'T, 'S>) =
map |> Seq.map snd

#endif
87 changes: 87 additions & 0 deletions tests/FSharpx.Collections.Tests/PersistentHashMapTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,91 @@ module PersistentHashMapTests =
!x
|> PersistentHashMap.containsKey((r.Next n).ToString())
|> Expect.isTrue "Next"
}

test "tryFind returns Some for existing key" {
let m = PersistentHashMap.ofSeq [ ("a", 1); ("b", 2) ]
Expect.equal "tryFind existing" (Some 1) (PersistentHashMap.tryFind "a" m)
}

test "tryFind returns None for missing key" {
let m = PersistentHashMap.ofSeq [ ("a", 1) ]
Expect.equal "tryFind missing" None (PersistentHashMap.tryFind "z" m)
}

test "filter keeps only matching entries" {
let m = PersistentHashMap.ofSeq [ (1, "a"); (2, "b"); (3, "c") ]
let result = PersistentHashMap.filter (fun k _ -> k > 1) m
Expect.equal "filter count" 2 (PersistentHashMap.count result)
Expect.isFalse "filter excludes 1" (PersistentHashMap.containsKey 1 result)
Expect.isTrue "filter keeps 2" (PersistentHashMap.containsKey 2 result)
}

test "iter visits all entries" {
let m = PersistentHashMap.ofSeq [ (1, 10); (2, 20); (3, 30) ]
let seen = System.Collections.Generic.HashSet<int>()
PersistentHashMap.iter (fun k _ -> seen.Add(k) |> ignore) m
Expect.equal "iter visits all" 3 seen.Count
}

test "fold sums all values" {
let m = PersistentHashMap.ofSeq [ ("a", 1); ("b", 2); ("c", 3) ]
let total = PersistentHashMap.fold (fun acc _ v -> acc + v) 0 m
Expect.equal "fold sum" 6 total
}

test "exists returns true when predicate matches" {
let m = PersistentHashMap.ofSeq [ (1, "x"); (2, "y") ]
Expect.isTrue "exists" (PersistentHashMap.exists (fun k _ -> k = 2) m)
}

test "exists returns false when no match" {
let m = PersistentHashMap.ofSeq [ (1, "x"); (2, "y") ]
Expect.isFalse "exists false" (PersistentHashMap.exists (fun k _ -> k = 99) m)
}

test "forall returns true when all entries match" {
let m = PersistentHashMap.ofSeq [ (1, 10); (2, 20) ]
Expect.isTrue "forall" (PersistentHashMap.forall (fun _ v -> v > 0) m)
}

test "forall returns false when some entry does not match" {
let m = PersistentHashMap.ofSeq [ (1, 10); (2, -1) ]
Expect.isFalse "forall false" (PersistentHashMap.forall (fun _ v -> v > 0) m)
}

test "choose keeps and transforms matching entries" {
let m = PersistentHashMap.ofSeq [ (1, 10); (2, 20); (3, 30) ]

let result =
PersistentHashMap.choose (fun _ v -> if v > 15 then Some(v * 2) else None) m

Expect.equal "choose count" 2 (PersistentHashMap.count result)
Expect.equal "choose value" 40 (PersistentHashMap.find 2 result)
}

test "toList round-trips via ofList" {
let pairs = [ (1, "one"); (2, "two"); (3, "three") ]
let m = PersistentHashMap.ofList pairs
let result = PersistentHashMap.toList m |> List.sortBy fst
Expect.equal "toList/ofList round-trip" (pairs |> List.sortBy fst) result
}

test "toArray round-trips via ofArray" {
let pairs = [| (1, "one"); (2, "two") |]
let m = PersistentHashMap.ofArray pairs
let result = PersistentHashMap.toArray m |> Array.sortBy fst
Expect.equal "toArray/ofArray round-trip" (pairs |> Array.sortBy fst) result
}

test "keys returns all keys" {
let m = PersistentHashMap.ofSeq [ (1, "a"); (2, "b"); (3, "c") ]
let ks = PersistentHashMap.keys m |> Seq.sort |> Seq.toList
Expect.equal "keys" [ 1; 2; 3 ] ks
}

test "values returns all values" {
let m = PersistentHashMap.ofSeq [ ("a", 1); ("b", 2); ("c", 3) ]
let vs = PersistentHashMap.values m |> Seq.sort |> Seq.toList
Expect.equal "values" [ 1; 2; 3 ] vs
} ]
Loading