@@ -30,18 +30,41 @@ extension Sequence {
30
30
@inlinable
31
31
public func keyed< Key> (
32
32
by keyForValue: ( Element ) throws -> Key ,
33
- // TODO: pass `Key` into `combine`: (Key, Element, Element) throws -> Element
34
- uniquingKeysWith combine: ( ( Element , Element ) throws -> Element ) ? = nil
33
+ uniquingKeysWith combine: ( ( Key , Element , Element ) throws -> Element ) ? = nil
35
34
) rethrows -> [ Key : Element ] {
36
- // Note: This implementation is a bit convoluted, but it's just aiming to reuse the existing stdlib logic,
37
- // to ensure consistent behaviour, error messages, etc.
38
- // If this API ends up in the stdlib itself, it could just call the underlying `_NativeDictionary` methods.
39
- try withoutActuallyEscaping ( keyForValue) { keyForValue in
40
- if let combine {
41
- return try Dictionary ( self . lazy. map { ( try keyForValue ( $0) , $0) } , uniquingKeysWith: combine)
42
- } else {
43
- return try Dictionary ( uniqueKeysWithValues: self . lazy. map { ( try keyForValue ( $0) , $0) } )
35
+ var result = [ Key: Element] ( )
36
+
37
+ if combine != nil {
38
+ // We have a `combine` closure. Use it to resolve duplicate keys.
39
+
40
+ for element in self {
41
+ let key = try keyForValue ( element)
42
+
43
+ if let oldValue = result. updateValue ( element, forKey: key) {
44
+ // Can't use a conditional binding to unwrap this, because the newly bound variable
45
+ // doesn't play nice with the `rethrows` system.
46
+ let valueToKeep = try combine!( key, oldValue, element)
47
+
48
+ // This causes a second look-up for the same key. The standard library can avoid that
49
+ // by calling `mutatingFind` to get access to the bucket where the value will end up,
50
+ // and updating in place.
51
+ // Swift Algorithms doesn't have access to that API, so we make due.
52
+ // When this gets merged into the standard library, we should optimize this.
53
+ result [ key] = valueToKeep
54
+ }
55
+ }
56
+ } else {
57
+ // There's no `combine` closure. Duplicate keys are disallowed.
58
+
59
+ for element in self {
60
+ let key = try keyForValue ( element)
61
+
62
+ guard result. updateValue ( element, forKey: key) == nil else {
63
+ fatalError ( " Duplicate values for key: ' \( key) ' " )
64
+ }
44
65
}
45
66
}
67
+
68
+ return result
46
69
}
47
70
}
0 commit comments