Last active
January 6, 2017 03:22
-
-
Save erica/b6a7351f5da8f5340d6134cf2deff439 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/// A "collection" of paired items | |
public struct AssociativeArray<PairedItem: Hashable>: CustomStringConvertible, ExpressibleByDictionaryLiteral { | |
/// Constructable outside module | |
public init() {} | |
public typealias DictForm = Dictionary<PairedItem, PairedItem> | |
/// The true 2-way association backing dictionary | |
private var backingDict: DictForm = [:] | |
/// The user-facing version which updates synchronously | |
/// so there is no overhead for user facing forms. | |
private var userFacingDict: DictForm = [:] | |
/// Supports thread safety | |
private var accessQueue = DispatchQueue(label: "org.sadun.2WayArray", attributes: [.concurrent]) | |
/// Allow paired access | |
public subscript(item1: PairedItem) -> PairedItem? { | |
get { | |
return accessQueue.sync { return backingDict[item1] } | |
} | |
set(newItem2) { | |
return accessQueue.sync(flags: [.barrier]) { | |
// No op for same value | |
let item2 = backingDict[item1] | |
guard item2 != newItem2 else { return } | |
switch (item2, newItem2) { | |
case (let item2?, let newItem2?): | |
// Sanitize newItem2 if paired | |
let newItem1 = backingDict[newItem2] | |
backingDict.removeValue(forKey: newItem2) | |
userFacingDict.removeValue(forKey: newItem2) | |
if newItem1 != nil { | |
let newItem1: PairedItem! = newItem1 | |
backingDict.removeValue(forKey: newItem1) | |
userFacingDict.removeValue(forKey: newItem1) | |
} | |
// Sanitize item2 | |
backingDict.removeValue(forKey: item2) | |
userFacingDict.removeValue(forKey: item2) | |
// Introduce new pair | |
backingDict[newItem2] = item1 | |
backingDict[item1] = newItem2 | |
userFacingDict[item1] = newItem2 | |
case (let item2?, nil): | |
backingDict.removeValue(forKey: item1) | |
backingDict.removeValue(forKey: item2) | |
userFacingDict.removeValue(forKey: item1) | |
userFacingDict.removeValue(forKey: item2) | |
case (nil, let newItem2?): | |
backingDict[newItem2] = item1 | |
backingDict[item1] = newItem2 | |
userFacingDict[item1] = newItem2 | |
default: | |
assertionFailure("Should never get here") | |
} | |
} | |
} | |
} | |
// Support dictionary literal conformance | |
public typealias Key = PairedItem | |
public typealias Value = PairedItem | |
/// Allows dictionary literal initialization | |
public init(dictionaryLiteral elements: (PairedItem, PairedItem)...) { | |
self.init() | |
for (item1, item2) in elements { | |
self[item1] = item2 | |
} | |
} | |
/// Returns reduced version with duplicates removed | |
public var dictionaryRepresentation: [PairedItem: PairedItem] { | |
return userFacingDict | |
} | |
/// Provides human-facing description | |
public var description: String { | |
return "\(dictionaryRepresentation)" | |
} | |
} | |
/// For public consumption in most common use case | |
public typealias PairedStrings = AssociativeArray<String> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
var test1 = AssociativeArray<String>() | |
test1["a"] = "b" | |
print(test1) | |
assert(test1["b"] == "a") | |
assert(test1["a"] == "b") | |
var test2: PairedStrings = [:] | |
print(test2) | |
var test3: PairedStrings = [ | |
"a": "b", | |
"c": "d", | |
"e": "f", | |
"d": "a" // removes "a":"b" and "c":"d" | |
] | |
assert(test3.dictionaryRepresentation == ["f": "e", "a": "d"]) | |
print(test3) | |
test3["b"] = "c" | |
assert(test3.dictionaryRepresentation == ["b": "c", "f": "e", "a": "d"]) | |
print(test3) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment