Skip to content

Commit 1a70760

Browse files
committed
Keep latest instead of throwing on collision
1 parent 4ca6551 commit 1a70760

File tree

3 files changed

+24
-42
lines changed

3 files changed

+24
-42
lines changed

Guides/Keyed.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,29 @@
66
Stores the elements of a sequence as the values of a Dictionary, keyed by the result of the given closure.
77

88
```swift
9-
let fruits = ["Apple", "Banana", "Cherry"]
10-
let fruitByLetter = try! fruits.keyed(by: { $0.first! })
9+
let fruits = ["Apricot", "Banana", "Apple", "Cherry", "Blackberry", "Avocado", "Coconut"]
10+
let fruitByLetter = fruits.keyed(by: { $0.first! })
1111
// Results in:
1212
// [
13-
// "A": "Apple",
14-
// "B": "Banana",
15-
// "C": "Cherry",
13+
// "A": "Avocado",
14+
// "B": "Blackberry",
15+
// "C": "Coconut",
1616
// ]
1717
```
1818

19-
Duplicate keys throw an error by default. Alternatively, you can provide a closure which specifies which value to keep:
19+
On a key-collision, the latest element is kept by default. Alternatively, you can provide a closure which specifies which value to keep:
2020

2121
```swift
2222
let fruits = ["Apricot", "Banana", "Apple", "Cherry", "Blackberry", "Avocado", "Coconut"]
2323
let fruitsByLetter = fruits.keyed(
2424
by: { $0.first! },
25-
resolvingConflictsWith: { key, old, new in new } // Always pick the latest fruit
25+
resolvingConflictsWith: { key, old, new in old } // Always pick the first fruit
2626
)
2727
// Results in:
2828
// [
29-
// "A": "Avocado",
30-
// "B": "Blackberry"],
31-
// "C": "Coconut",
29+
// "A": "Apricot",
30+
// "B": "Banana",
31+
// "C": "Cherry",
3232
// ]
3333
```
3434

@@ -40,7 +40,7 @@ The `keyed(by:)` and `keyed(by:resolvingConflictsWith:)` methods are declared in
4040
extension Sequence {
4141
public func keyed<Key>(
4242
by keyForValue: (Element) throws -> Key
43-
) throws -> [Key: Element]
43+
) rethrows -> [Key: Element]
4444

4545
public func keyed<Key>(
4646
by keyForValue: (Element) throws -> Key,

Sources/Algorithms/Keyed.swift

100644100755
Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,20 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12-
public struct KeysAreNotUnique<Key, Element>: Error {
13-
public let key: Key
14-
public let previousElement: Element
15-
public let conflictingElement: Element
16-
17-
@inlinable
18-
public init(key: Key, previousElement: Element, conflictingElement: Element) {
19-
self.key = key
20-
self.previousElement = previousElement
21-
self.conflictingElement = conflictingElement
22-
}
23-
}
24-
2512
extension Sequence {
2613
/// Creates a new Dictionary from the elements of `self`, keyed by the
27-
/// results returned by the given `keyForValue` closure. Deriving the
28-
/// same duplicate key for more than one element of `self` will cause
29-
/// an error to be thrown.
14+
/// results returned by the given `keyForValue` closure.
15+
///
16+
/// If the key derived for a new element collides with an existing key from a previous element,
17+
/// the latest value will be kept.
3018
///
3119
/// - Parameters:
3220
/// - keyForValue: A closure that returns a key for each element in `self`.
33-
/// - Throws: `KeysAreNotUnique ` if two values map to the same key (via `keyForValue`).
3421
@inlinable
3522
public func keyed<Key>(
3623
by keyForValue: (Element) throws -> Key
37-
) throws -> [Key: Element] {
38-
try self.keyed(by: keyForValue, resolvingConflictsWith: {
39-
throw KeysAreNotUnique(key: $0, previousElement: $1, conflictingElement: $2)
40-
})
24+
) rethrows -> [Key: Element] {
25+
return try self.keyed(by: keyForValue, resolvingConflictsWith: { _, old, new in new })
4126
}
4227

4328
/// Creates a new Dictionary from the elements of `self`, keyed by the

Tests/SwiftAlgorithmsTests/KeyedTests.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ final class KeyedTests: XCTestCase {
1616
private class SampleError: Error {}
1717

1818
func testUniqueKeys() {
19-
let d = try! ["Apple", "Banana", "Cherry"].keyed(by: { $0.first! })
19+
let d = ["Apple", "Banana", "Cherry"].keyed(by: { $0.first! })
2020
XCTAssertEqual(d.count, 3)
2121
XCTAssertEqual(d["A"]!, "Apple")
2222
XCTAssertEqual(d["B"]!, "Banana")
@@ -25,19 +25,16 @@ final class KeyedTests: XCTestCase {
2525
}
2626

2727
func testEmpty() {
28-
let d = try! EmptyCollection<String>().keyed(by: { $0.first! })
28+
let d = EmptyCollection<String>().keyed(by: { $0.first! })
2929
XCTAssertEqual(d.count, 0)
3030
}
3131

3232
func testNonUniqueKeys() throws {
33-
XCTAssertThrowsError(
34-
try ["Apple", "Avocado", "Banana", "Cherry"].keyed(by: { $0.first! })
35-
) { thrownError in
36-
let e = thrownError as! KeysAreNotUnique<Character, String>
37-
XCTAssertEqual(e.key, "A")
38-
XCTAssertEqual(e.previousElement, "Apple")
39-
XCTAssertEqual(e.conflictingElement, "Avocado")
40-
}
33+
let d = ["Apple", "Avocado", "Banana", "Cherry"].keyed(by: { $0.first! })
34+
XCTAssertEqual(d.count, 3)
35+
XCTAssertEqual(d["A"]!, "Avocado", "On a key-collision, keyed(by:) should take the latest value.")
36+
XCTAssertEqual(d["B"]!, "Banana")
37+
XCTAssertEqual(d["C"]!, "Cherry")
4138
}
4239

4340
func testNonUniqueKeysWithMergeFunction() {

0 commit comments

Comments
 (0)