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
4 changes: 4 additions & 0 deletions docs/release-notes/.FSharp.Core/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
* Fix `Array.exists2` documentation examples to use equal-length arrays; the previous examples would throw `ArgumentException` at runtime instead of returning the documented `false`/`true` values. ([PR #19672](https://github.com/dotnet/fsharp/pull/19672))
* Move `Async.StartChild` to the "Starting Async Computations" docs category alongside `Async.StartChildAsTask`. ([Issue #19667](https://github.com/dotnet/fsharp/issues/19667))
* Add `InlineIfLambda` to `Array.init` ([PR #19869](https://github.com/dotnet/fsharp/pull/19869))

### Added

* Added `Map.merge`, which combines two maps into one, using a function to resolve the values of keys present in both. ([Suggestion #560](https://github.com/fsharp/fslang-suggestions/issues/560), [PR #19994](https://github.com/dotnet/fsharp/pull/19994))
9 changes: 9 additions & 0 deletions src/FSharp.Core/map.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,15 @@ module Map =
let foldBack<'Key, 'T, 'State when 'Key: comparison> folder (table: Map<'Key, 'T>) (state: 'State) =
MapTree.foldBack folder table.Tree state

[<CompiledName("Merge")>]
let merge resolve (table1: Map<_, _>) (table2: Map<_, _>) =
(table1, table2)
||> fold (fun acc key value2 ->
acc
|> change key (function
| Some value1 -> Some(resolve key value1 value2)
| None -> Some value2))

[<CompiledName("ToSeq")>]
let toSeq (table: Map<_, _>) =
table |> Seq.map (fun kvp -> kvp.Key, kvp.Value)
Expand Down
23 changes: 23 additions & 0 deletions src/FSharp.Core/map.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,29 @@ module Map =
[<CompiledName("Map")>]
val map: mapping: ('Key -> 'T -> 'U) -> table: Map<'Key, 'T> -> Map<'Key, 'U>

/// <summary>Builds a new map that contains the bindings of the two given maps. When a key occurs in
/// both maps, the given function is used to combine the two values into one.</summary>
///
/// <param name="resolve">The function used to combine the values for keys that occur in both maps. It is
/// passed the key and the corresponding values from the first and second map respectively.</param>
/// <param name="table1">The first input map.</param>
/// <param name="table2">The second input map.</param>
///
/// <returns>The merged map.</returns>
///
/// <remarks>This is an O(m log n) operation, where m is the size of the second map and n is the size of the merged map.</remarks>
///
/// <example id="merge-1">
/// <code lang="fsharp">
/// let sample1 = Map [ (1, 1); (2, 2) ]
/// let sample2 = Map [ (2, 20); (3, 30) ]
///
/// (sample1, sample2) ||> Map.merge (fun _key v1 v2 -> v1 + v2) // evaluates to map [(1, 1); (2, 22); (3, 30)]
/// </code>
/// </example>
[<CompiledName("Merge")>]
val merge: resolve: ('Key -> 'T -> 'T -> 'T) -> table1: Map<'Key, 'T> -> table2: Map<'Key, 'T> -> Map<'Key, 'T>

/// <summary>Tests if an element is in the domain of the map.</summary>
///
/// <param name="key">The input key.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Change[TKey,T](TKey, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.FSharpOption`1[T],Microsoft.FSharp.Core.FSharpOption`1[T]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Empty[TKey,T]()
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Filter[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Merge[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfArray[TKey,T](System.Tuple`2[TKey,T][])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfList[TKey,T](Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[TKey,T]])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfSeq[TKey,T](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,T]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Change[TKey,T](TKey, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.FSharpOption`1[T],Microsoft.FSharp.Core.FSharpOption`1[T]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Empty[TKey,T]()
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Filter[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Merge[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfArray[TKey,T](System.Tuple`2[TKey,T][])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfList[TKey,T](Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[TKey,T]])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfSeq[TKey,T](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,T]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Change[TKey,T](TKey, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.FSharpOption`1[T],Microsoft.FSharp.Core.FSharpOption`1[T]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Empty[TKey,T]()
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Filter[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Merge[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfArray[TKey,T](System.Tuple`2[TKey,T][])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfList[TKey,T](Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[TKey,T]])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfSeq[TKey,T](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,T]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Change[TKey,T](TKey, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.FSharpOption`1[T],Microsoft.FSharp.Core.FSharpOption`1[T]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Empty[TKey,T]()
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Filter[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] Merge[TKey,T](Microsoft.FSharp.Core.FSharpFunc`2[TKey,Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[T,T]]], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T], Microsoft.FSharp.Collections.FSharpMap`2[TKey,T])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfArray[TKey,T](System.Tuple`2[TKey,T][])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfList[TKey,T](Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`2[TKey,T]])
Microsoft.FSharp.Collections.MapModule: Microsoft.FSharp.Collections.FSharpMap`2[TKey,T] OfSeq[TKey,T](System.Collections.Generic.IEnumerable`1[System.Tuple`2[TKey,T]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,38 @@ type MapModule() =

()

[<Fact>]
member _.Merge() =

// value keys, with both overlapping and disjoint keys
let valueKeyMap1 = Map.ofSeq [(1, 1); (2, 2); (3, 3)]
let valueKeyMap2 = Map.ofSeq [(2, 20); (3, 30); (4, 40)]
let resultValueMap = (valueKeyMap1, valueKeyMap2) ||> Map.merge (fun _ v1 v2 -> v1 + v2)
Assert.AreEqual([(1, 1); (2, 22); (3, 33); (4, 40)] |> Map.ofList, resultValueMap)

// the resolver is passed the key and the values from the first and second map respectively
let left = Map.ofList [("a", "L")]
let right = Map.ofList [("a", "R")]
let resultOrder = (left, right) ||> Map.merge (fun k v1 v2 -> k + v1 + v2)
Assert.AreEqual(Map.ofList [("a", "aLR")], resultOrder)

// reference keys
let refMap1 = Map.ofSeq [for c in ["."; ".."; "..."] do yield (c, c.Length)]
let refMap2 = Map.ofSeq [for c in [".."; "..."; "...."] do yield (c, c.Length * 10)]
let resultRefMap = (refMap1, refMap2) ||> Map.merge (fun _ v1 v2 -> v1 + v2)
Assert.AreEqual([(".", 1); ("..", 22); ("...", 33); ("....", 40)] |> Map.ofList, resultRefMap)

// merging with an empty map returns the other map unchanged
let oeleMap = Map.ofSeq [(1, "one")]
let eptMap = Map.empty<int, string>
Assert.AreEqual(oeleMap, (oeleMap, eptMap) ||> Map.merge (fun _ v1 v2 -> v1 + v2))
Assert.AreEqual(oeleMap, (eptMap, oeleMap) ||> Map.merge (fun _ v1 v2 -> v1 + v2))

// both empty
Assert.AreEqual(eptMap, (eptMap, eptMap) ||> Map.merge (fun _ v1 v2 -> v1 + v2))

()

[<Fact>]
member _.Contains() =
// value keys
Expand Down
Loading