Skip to content

Commit 41ea7e2

Browse files
authored
Merge pull request #10113 from natecook1000/nc-dict-tuples
[stdlib] Add dictionary overloads for merging methods
2 parents eb1fcc4 + a48e7fd commit 41ea7e2

File tree

2 files changed

+218
-19
lines changed

2 files changed

+218
-19
lines changed

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,13 +1730,13 @@ public struct Dictionary<Key : Hashable, Value> :
17301730
/// print(wordToValue)
17311731
/// // Prints "["three": 3, "four": 4, "five": 5, "one": 1, "two": 2]"
17321732
///
1733-
/// - Parameter keysAndValues: A sequence of `(Key, Value)` tuples to use for
1733+
/// - Parameter keysAndValues: A sequence of key-value pairs to use for
17341734
/// the new dictionary. Every key in `keysAndValues` must be unique.
17351735
/// - Returns: A new dictionary initialized with the elements of
17361736
/// `keysAndValues`.
17371737
public init<S: Sequence>(
17381738
uniqueKeysWithValues keysAndValues: S
1739-
) where S.Iterator.Element == (Key, Value) {
1739+
) where S.Element == (Key, Value) {
17401740
if let d = keysAndValues as? Dictionary<Key, Value> {
17411741
self = d
17421742
} else {
@@ -1773,15 +1773,15 @@ public struct Dictionary<Key : Hashable, Value> :
17731773
/// // ["b": 4, "a": 3]
17741774
///
17751775
/// - Parameters:
1776-
/// - keysAndValues: A sequence of `(Key, Value)` tuples to use for the new
1776+
/// - keysAndValues: A sequence of key-value pairs to use for the new
17771777
/// dictionary.
17781778
/// - combine: A closure that is called with the values for any duplicate
17791779
/// keys that are encountered. The closure returns the desired value for
17801780
/// the final dictionary.
17811781
public init<S: Sequence>(
17821782
_ keysAndValues: S,
17831783
uniquingKeysWith combine: (Value, Value) throws -> Value
1784-
) rethrows where S.Iterator.Element == (Key, Value) {
1784+
) rethrows where S.Element == (Key, Value) {
17851785
self = Dictionary(minimumCapacity: keysAndValues.underestimatedCount)
17861786
try _variantBuffer.merge(keysAndValues, uniquingKeysWith: combine)
17871787
}
@@ -1810,8 +1810,8 @@ public struct Dictionary<Key : Hashable, Value> :
18101810
/// `values`.
18111811
public init<S: Sequence>(
18121812
grouping values: S,
1813-
by keyForValue: (S.Iterator.Element) throws -> Key
1814-
) rethrows where Value == [S.Iterator.Element] {
1813+
by keyForValue: (S.Element) throws -> Key
1814+
) rethrows where Value == [S.Element] {
18151815
self = [:]
18161816
for value in values {
18171817
self[try keyForValue(value), default: []].append(value)
@@ -2093,27 +2093,60 @@ public struct Dictionary<Key : Hashable, Value> :
20932093
/// var dictionary = ["a": 1, "b": 2]
20942094
///
20952095
/// // Keeping existing value for key "a":
2096-
/// dictionary.merge(["a": 3, "c": 4])
2097-
/// { (current, _) in current }
2096+
/// dictionary.merge(zip(["a", "c"], [3, 4])) { (current, _) in current }
20982097
/// // ["b": 2, "a": 1, "c": 4]
20992098
///
21002099
/// // Taking the new value for key "a":
2101-
/// dictionary.merge(["a": 5, "d": 6])
2102-
/// { (_, new) in new }
2100+
/// dictionary.merge(zip(["a", "d"], [5, 6])) { (_, new) in new }
21032101
/// // ["b": 2, "a": 5, "c": 4, "d": 6]
21042102
///
21052103
/// - Parameters:
2106-
/// - other: A sequence of `(Key, Value)` tuples.
2104+
/// - other: A sequence of key-value pairs.
21072105
/// - combine: A closure that takes the current and new values for any
21082106
/// duplicate keys. The closure returns the desired value for the final
21092107
/// dictionary.
21102108
public mutating func merge<S: Sequence>(
21112109
_ other: S,
21122110
uniquingKeysWith combine: (Value, Value) throws -> Value
2113-
) rethrows where S.Iterator.Element == (Key, Value) {
2111+
) rethrows where S.Element == (Key, Value) {
21142112
try _variantBuffer.merge(other, uniquingKeysWith: combine)
21152113
}
21162114

2115+
/// Merges the given dictionary into this dictionary, using a combining
2116+
/// closure to determine the value for any duplicate keys.
2117+
///
2118+
/// Use the `combine` closure to select which value to use in the updated
2119+
/// dictionary, or to combine existing and new values. As the key-values
2120+
/// pairs in `other` are merged with this dictionary, the `combine` closure
2121+
/// is called with the current and new values for any duplicate keys that
2122+
/// are encountered.
2123+
///
2124+
/// This example shows how to choose the current or new values for any
2125+
/// duplicate keys:
2126+
///
2127+
/// var dictionary = ["a": 1, "b": 2]
2128+
///
2129+
/// // Keeping existing value for key "a":
2130+
/// dictionary.merge(["a": 3, "c": 4]) { (current, _) in current }
2131+
/// // ["b": 2, "a": 1, "c": 4]
2132+
///
2133+
/// // Taking the new value for key "a":
2134+
/// dictionary.merge(["a": 5, "d": 6]) { (_, new) in new }
2135+
/// // ["b": 2, "a": 5, "c": 4, "d": 6]
2136+
///
2137+
/// - Parameters:
2138+
/// - other: A dictionary to merge.
2139+
/// - combine: A closure that takes the current and new values for any
2140+
/// duplicate keys. The closure returns the desired value for the final
2141+
/// dictionary.
2142+
public mutating func merge(
2143+
_ other: [Key: Value],
2144+
uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows
2145+
{
2146+
try _variantBuffer.merge(
2147+
other.lazy.map { ($0, $1) }, uniquingKeysWith: combine)
2148+
}
2149+
21172150
/// Returns a new dictionary created by merging the key-value pairs in the
21182151
/// given sequence into the dictionary, using a combining closure to
21192152
/// determine the value for any duplicate keys.
@@ -2128,7 +2161,45 @@ public struct Dictionary<Key : Hashable, Value> :
21282161
/// duplicate keys:
21292162
///
21302163
/// let dictionary = ["a": 1, "b": 2]
2164+
/// let newKeyValues = zip(["a", "b"], [3, 4])
2165+
///
2166+
/// let keepingCurrent = dictionary.merging(newKeyValues) { (current, _) in current }
2167+
/// // ["b": 2, "a": 1]
2168+
/// let replacingCurrent = dictionary.merging(newKeyValues) { (_, new) in new }
2169+
/// // ["b": 4, "a": 3]
2170+
///
2171+
/// - Parameters:
2172+
/// - other: A sequence of key-value pairs.
2173+
/// - combine: A closure that takes the current and new values for any
2174+
/// duplicate keys. The closure returns the desired value for the final
2175+
/// dictionary.
2176+
/// - Returns: A new dictionary with the combined keys and values of this
2177+
/// dictionary and `other`.
2178+
public func merging<S: Sequence>(
2179+
_ other: S,
2180+
uniquingKeysWith combine: (Value, Value) throws -> Value
2181+
) rethrows -> [Key: Value] where S.Element == (Key, Value) {
2182+
var result = self
2183+
try result._variantBuffer.merge(other, uniquingKeysWith: combine)
2184+
return result
2185+
}
2186+
2187+
/// Returns a new dictionary created by merging the given dictionary into
2188+
/// this dictionary, using a combining closure to determine the value for
2189+
/// any duplicate keys.
2190+
///
2191+
/// Use the `combine` closure to select which value to use in the returned
2192+
/// dictionary, or to combine existing and new values. As the key-value
2193+
/// pairs in `other` are merged with this dictionary, the `combine` closure
2194+
/// is called with the current and new values for any duplicate keys that
2195+
/// are encountered.
2196+
///
2197+
/// This example shows how to choose the current or new values for any
2198+
/// duplicate keys:
2199+
///
2200+
/// let dictionary = ["a": 1, "b": 2]
21312201
/// let otherDictionary = ["a": 3, "b": 4]
2202+
///
21322203
/// let keepingCurrent = dictionary.merging(otherDictionary)
21332204
/// { (current, _) in current }
21342205
/// // ["b": 2, "a": 1]
@@ -2137,16 +2208,16 @@ public struct Dictionary<Key : Hashable, Value> :
21372208
/// // ["b": 4, "a": 3]
21382209
///
21392210
/// - Parameters:
2140-
/// - other: A sequence of `(Key, Value)` tuples.
2211+
/// - other: A dictionary to merge.
21412212
/// - combine: A closure that takes the current and new values for any
21422213
/// duplicate keys. The closure returns the desired value for the final
21432214
/// dictionary.
21442215
/// - Returns: A new dictionary with the combined keys and values of this
21452216
/// dictionary and `other`.
2146-
public func merging<S: Sequence>(
2147-
_ other: S,
2217+
public func merging(
2218+
_ other: [Key: Value],
21482219
uniquingKeysWith combine: (Value, Value) throws -> Value
2149-
) rethrows -> [Key: Value] where S.Iterator.Element == (Key, Value) {
2220+
) rethrows -> [Key: Value] {
21502221
var result = self
21512222
try result.merge(other, uniquingKeysWith: combine)
21522223
return result
@@ -4938,7 +5009,7 @@ internal enum _Variant${Self}Buffer<${TypeParametersDecl}> : _HashBuffer {
49385009
internal mutating func nativeMerge<S: Sequence>(
49395010
_ keysAndValues: S,
49405011
uniquingKeysWith combine: (Value, Value) throws -> Value
4941-
) rethrows where S.Iterator.Element == (Key, Value) {
5012+
) rethrows where S.Element == (Key, Value) {
49425013
for (key, value) in keysAndValues {
49435014
var (i, found) = asNative._find(key, startBucket: asNative._bucket(key))
49445015

@@ -4969,7 +5040,7 @@ internal enum _Variant${Self}Buffer<${TypeParametersDecl}> : _HashBuffer {
49695040
internal mutating func merge<S: Sequence>(
49705041
_ keysAndValues: S,
49715042
uniquingKeysWith combine: (Value, Value) throws -> Value
4972-
) rethrows where S.Iterator.Element == (Key, Value) {
5043+
) rethrows where S.Element == (Key, Value) {
49735044
if _fastPath(guaranteedNative) {
49745045
try nativeMerge(keysAndValues, uniquingKeysWith: combine)
49755046
return

validation-test/stdlib/Dictionary.swift

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ DictionaryTestSuite.test("COW.Slow.AddDoesNotReallocate") {
477477
}
478478
}
479479

480-
DictionaryTestSuite.test("COW.Fast.MergeDoesNotReallocate") {
480+
DictionaryTestSuite.test("COW.Fast.MergeSequenceDoesNotReallocate") {
481481
do {
482482
var d1 = getCOWFastDictionary()
483483
var identity1 = d1._rawIdentifier()
@@ -501,6 +501,13 @@ DictionaryTestSuite.test("COW.Fast.MergeDoesNotReallocate") {
501501
assert(d1.count == 7)
502502
assert(d1[30]! == 1030)
503503
assert(d1[70]! == 2070)
504+
505+
let d2 = d1.merging([(40, 3040), (80, 3080)]) { _, y in y }
506+
assert(identity1 == d1._rawIdentifier())
507+
assert(identity1 != d2._rawIdentifier())
508+
assert(d2.count == 8)
509+
assert(d2[40]! == 3040)
510+
assert(d2[80]! == 3080)
504511
}
505512

506513
do {
@@ -591,6 +598,127 @@ DictionaryTestSuite.test("COW.Fast.MergeDoesNotReallocate") {
591598
}
592599
}
593600

601+
DictionaryTestSuite.test("COW.Fast.MergeDictionaryDoesNotReallocate") {
602+
do {
603+
var d1 = getCOWFastDictionary()
604+
var identity1 = d1._rawIdentifier()
605+
606+
// Merge some new values.
607+
d1.merge([40: 2040, 50: 2050]) { _, y in y }
608+
assert(identity1 == d1._rawIdentifier())
609+
assert(d1.count == 5)
610+
assert(d1[50]! == 2050)
611+
612+
// Merge and overwrite some existing values.
613+
d1.merge([10: 2010, 60: 2060]) { _, y in y }
614+
assert(identity1 == d1._rawIdentifier())
615+
assert(d1.count == 6)
616+
assert(d1[10]! == 2010)
617+
assert(d1[60]! == 2060)
618+
619+
// Merge, keeping existing values.
620+
d1.merge([30: 2030, 70: 2070]) { x, _ in x }
621+
assert(identity1 == d1._rawIdentifier())
622+
assert(d1.count == 7)
623+
assert(d1[30]! == 1030)
624+
assert(d1[70]! == 2070)
625+
626+
let d2 = d1.merging([40: 3040, 80: 3080]) { _, y in y }
627+
assert(identity1 == d1._rawIdentifier())
628+
assert(identity1 != d2._rawIdentifier())
629+
assert(d2.count == 8)
630+
assert(d2[40]! == 3040)
631+
assert(d2[80]! == 3080)
632+
}
633+
634+
do {
635+
var d1 = getCOWFastDictionary()
636+
var identity1 = d1._rawIdentifier()
637+
638+
var d2 = d1
639+
assert(identity1 == d1._rawIdentifier())
640+
assert(identity1 == d2._rawIdentifier())
641+
642+
// Merge some new values.
643+
d2.merge([40: 2040, 50: 2050]) { _, y in y }
644+
assert(identity1 == d1._rawIdentifier())
645+
assert(identity1 != d2._rawIdentifier())
646+
647+
assert(d1.count == 3)
648+
assert(d1[10]! == 1010)
649+
assert(d1[20]! == 1020)
650+
assert(d1[30]! == 1030)
651+
assert(d1[40] == nil)
652+
653+
assert(d2.count == 5)
654+
assert(d2[10]! == 1010)
655+
assert(d2[20]! == 1020)
656+
assert(d2[30]! == 1030)
657+
assert(d2[40]! == 2040)
658+
assert(d2[50]! == 2050)
659+
660+
// Keep variables alive.
661+
_fixLifetime(d1)
662+
_fixLifetime(d2)
663+
}
664+
665+
do {
666+
var d1 = getCOWFastDictionary()
667+
var identity1 = d1._rawIdentifier()
668+
669+
var d2 = d1
670+
assert(identity1 == d1._rawIdentifier())
671+
assert(identity1 == d2._rawIdentifier())
672+
673+
// Merge and overwrite some existing values.
674+
d2.merge([10: 2010]) { _, y in y }
675+
assert(identity1 == d1._rawIdentifier())
676+
assert(identity1 != d2._rawIdentifier())
677+
678+
assert(d1.count == 3)
679+
assert(d1[10]! == 1010)
680+
assert(d1[20]! == 1020)
681+
assert(d1[30]! == 1030)
682+
683+
assert(d2.count == 3)
684+
assert(d2[10]! == 2010)
685+
assert(d2[20]! == 1020)
686+
assert(d2[30]! == 1030)
687+
688+
// Keep variables alive.
689+
_fixLifetime(d1)
690+
_fixLifetime(d2)
691+
}
692+
693+
do {
694+
var d1 = getCOWFastDictionary()
695+
var identity1 = d1._rawIdentifier()
696+
697+
var d2 = d1
698+
assert(identity1 == d1._rawIdentifier())
699+
assert(identity1 == d2._rawIdentifier())
700+
701+
// Merge, keeping existing values.
702+
d2.merge([10: 2010]) { x, _ in x }
703+
assert(identity1 == d1._rawIdentifier())
704+
assert(identity1 != d2._rawIdentifier())
705+
706+
assert(d1.count == 3)
707+
assert(d1[10]! == 1010)
708+
assert(d1[20]! == 1020)
709+
assert(d1[30]! == 1030)
710+
711+
assert(d2.count == 3)
712+
assert(d2[10]! == 1010)
713+
assert(d2[20]! == 1020)
714+
assert(d2[30]! == 1030)
715+
716+
// Keep variables alive.
717+
_fixLifetime(d1)
718+
_fixLifetime(d2)
719+
}
720+
}
721+
594722
DictionaryTestSuite.test("COW.Fast.DefaultedSubscriptDoesNotReallocate") {
595723
do {
596724
var d1 = getCOWFastDictionary()

0 commit comments

Comments
 (0)