Skip to content

Commit 87631ee

Browse files
committed
Clean Up The Interface to BidirectionalMap
Minimize the interface, split and document the updateValue entrypoints, and redefine them in terms of their subscript counterparts.
1 parent 3f2dc68 commit 87631ee

File tree

2 files changed

+52
-39
lines changed

2 files changed

+52
-39
lines changed

Sources/SwiftDriver/IncrementalCompilation/BidirectionalMap.swift

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
/// Like a two-way dictionary
13+
/// Provides a bidirectional mapping between two keys.
1414
///
15-
public struct BidirectionalMap<T1: Hashable, T2: Hashable>: Equatable, Sequence {
15+
/// `BidirectionalMap` provides efficient O(1) lookups in both directions.
16+
public struct BidirectionalMap<T1: Hashable, T2: Hashable>: Equatable {
1617
private var map1: [T1: T2] = [:]
1718
private var map2: [T2: T1] = [:]
1819

@@ -63,40 +64,53 @@ public struct BidirectionalMap<T1: Hashable, T2: Hashable>: Equatable, Sequence
6364
}
6465
}
6566
}
67+
}
6668

67-
public func contains(key: T1) -> Bool {
68-
map1.keys.contains(key)
69-
}
70-
public func contains(key: T2) -> Bool {
71-
map2.keys.contains(key)
72-
}
73-
69+
extension BidirectionalMap {
70+
/// Updates the value stored in the bidirectional map for the given key,
71+
/// or adds a new set of key-value pairs if the key have an entry in the map.
72+
///
73+
/// If access to the old value is not necessary, it is more efficient to use
74+
/// the subscript operator to perform an in-place update.
75+
///
76+
/// - Parameters:
77+
/// - v: The new value to add to the two-level map.
78+
/// - key: The two-level key to associate with value.
79+
/// - Returns: The value that was replaced, or `nil` if a new key-value pair was added.
80+
@discardableResult
7481
public mutating func updateValue(_ newValue: T2, forKey key: T1) -> T2? {
75-
let oldValue = map1.updateValue(newValue, forKey: key)
76-
_ = oldValue.map {map2.removeValue(forKey: $0)}
77-
map2[newValue] = key
78-
return oldValue
82+
let old = self[key]
83+
self[key] = newValue
84+
return old
7985
}
86+
87+
/// Updates the value stored in the bidirectional map for the given key,
88+
/// or adds a new set of key-value pairs if the key have an entry in the map.
89+
///
90+
/// If access to the old value is not necessary, it is more efficient to use
91+
/// the subscript operator to perform an in-place update.
92+
///
93+
/// - Parameters:
94+
/// - v: The new value to add to the two-level map.
95+
/// - key: The two-level key to associate with value.
96+
/// - Returns: The value that was replaced, or `nil` if a new key-value pair was added.
97+
@discardableResult
8098
public mutating func updateValue(_ newValue: T1, forKey key: T2) -> T1? {
81-
let oldValue = map2.updateValue(newValue, forKey: key)
82-
_ = oldValue.map {map1.removeValue(forKey: $0)}
83-
map1[newValue] = key
84-
return oldValue
99+
let old = self[key]
100+
self[key] = newValue
101+
return old
85102
}
103+
}
86104

87-
public mutating func removeValue(forKey t1: T1) {
88-
if let t2 = map1[t1] {
89-
map2.removeValue(forKey: t2)
90-
}
91-
map1.removeValue(forKey: t1)
92-
}
93-
public mutating func removeValue(forKey t2: T2) {
94-
if let t1 = map2[t2] {
95-
map1.removeValue(forKey: t1)
96-
}
97-
map2.removeValue(forKey: t2)
98-
}
105+
extension BidirectionalMap: Sequence {
106+
/// Provides an iterator that yields pairs of the key-to-key mappings.
107+
///
108+
/// - Warning: The order of the returned mappings is not stable. In general,
109+
/// avoid iterating over a bidirectional map unless order does not
110+
/// matter for the algorithm in question.
111+
///
112+
/// - Returns: An iterator value for this bidirectional map.
99113
public func makeIterator() -> Dictionary<T1, T2>.Iterator {
100-
map1.makeIterator()
114+
self.map1.makeIterator()
101115
}
102116
}

Tests/SwiftDriverTests/BidirectionalMapTests.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import XCTest
1414
import SwiftDriver
1515

1616
class BidirectionalMapTests: XCTestCase {
17-
1817
func testBiDiMap() {
1918
func test(_ biMapToTest: BidirectionalMap<Int, String>) {
2019
zip(biMapToTest.map{$0}.sorted {$0.0 < $1.0}, testContents).forEach {
@@ -24,10 +23,10 @@ class BidirectionalMapTests: XCTestCase {
2423
for (i, s) in testContents.map({$0}) {
2524
XCTAssertEqual(biMapToTest[i], s)
2625
XCTAssertEqual(biMapToTest[s], i)
27-
XCTAssertTrue(biMapToTest.contains(key: i))
28-
XCTAssertTrue(biMapToTest.contains(key: s))
29-
XCTAssertFalse(biMapToTest.contains(key: -1))
30-
XCTAssertFalse(biMapToTest.contains(key: "gazorp"))
26+
XCTAssertNotNil(biMapToTest[i])
27+
XCTAssertNotNil(biMapToTest[s])
28+
XCTAssertNil(biMapToTest[-1])
29+
XCTAssertNil(biMapToTest["gazorp"])
3130
}
3231
}
3332

@@ -37,15 +36,15 @@ class BidirectionalMapTests: XCTestCase {
3736
biMap[i] = s
3837
}
3938
test(biMap)
40-
biMap.removeValue(forKey: testContents.count)
39+
biMap[testContents.count] = nil
4140
test(biMap)
42-
biMap.removeValue(forKey: "gazorp")
41+
biMap["gazorp"] = nil
4342
test(biMap)
4443

4544
let removed = testContents.removeFirst()
4645
var biMap2 = biMap
47-
biMap.removeValue(forKey: removed.0)
48-
biMap2.removeValue(forKey: removed.1)
46+
biMap[removed.0] = nil
47+
biMap2[removed.1] = nil
4948
test(biMap)
5049
test(biMap2)
5150
}

0 commit comments

Comments
 (0)