Last active
February 21, 2019 14:44
-
-
Save DevAndArtist/d8ed2b96cb11ff4a758571f176377275 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
public struct Unique<Key, Value>: Hashable where Key: Hashable { | |
public enum KeyVariant { | |
case key(Key) | |
case keyPath(KeyPath<Value, Key>) | |
case closure((Value) -> Key) | |
internal func _key(for value: Value) -> Key { | |
switch self { | |
case .key(let key): | |
return key | |
case .keyPath(let keyPath): | |
return value[keyPath: keyPath] | |
case .closure(let closure): | |
return closure(value) | |
} | |
} | |
} | |
private let _keyVariant: KeyVariant | |
private var _value: Value | |
private init(_keyVariant: KeyVariant, value: Value) { | |
self._keyVariant = _keyVariant | |
self._value = value | |
} | |
public init(key: Key, value: Value) { | |
self.init(_keyVariant: .key(key), value: value) | |
} | |
public init(keyPath: KeyPath<Value, Key>, value: Value) { | |
self.init(_keyVariant: .keyPath(keyPath), value: value) | |
} | |
public init(closure: @escaping (Value) -> Key, value: Value) { | |
self.init(_keyVariant: .closure(closure), value: value) | |
} | |
public var key: Key { | |
return _keyVariant._key(for: _value) | |
} | |
public var value: Value { | |
get { return _value } | |
set { | |
precondition(_hasIdenticalKey(with: newValue)) | |
_value = newValue | |
} | |
} | |
public static func == (lhs: Unique, rhs: Unique) -> Bool { | |
return lhs.key == rhs.key | |
} | |
public func hash(into hasher: inout Hasher) { | |
hasher.combine(key) | |
} | |
public func map<NewValue>( | |
_ transform: (Value) throws -> NewValue | |
) rethrows -> Unique<Key, NewValue> { | |
let newValue = try transform(_value) | |
return Unique<Key, NewValue>(key: key, value: newValue) | |
} | |
public func mapKey<NewKey>( | |
_ transform: (KeyVariant) throws -> Unique<NewKey, Value>.KeyVariant | |
) rethrows -> Unique<NewKey, Value> { | |
let newKeyVariant = try transform(_keyVariant) | |
return Unique<NewKey, Value>(_keyVariant: newKeyVariant, value: _value) | |
} | |
} | |
extension Unique { | |
private func _hasIdenticalKey(with other: Value) -> Bool { | |
return _keyVariant._key(for: _value) == _keyVariant._key(for: other) | |
} | |
} | |
public protocol Uniquifiable { | |
typealias UniquifiedBy<Key: Hashable> = Unique<Key, Self> | |
func uniquify<Key: Hashable>(using key: Key) -> UniquifiedBy<Key> | |
func uniquify<Key: Hashable>( | |
using keyPath: KeyPath<Self, Key> | |
) -> UniquifiedBy<Key> | |
func uniquify<Key: Hashable>( | |
using closure: @escaping (Self) -> Key | |
) -> UniquifiedBy<Key> | |
} | |
extension Uniquifiable { | |
public func uniquify<Key: Hashable>(using key: Key) -> UniquifiedBy<Key> { | |
return UniquifiedBy(key: key, value: self) | |
} | |
public func uniquify<Key: Hashable>( | |
using keyPath: KeyPath<Self, Key> | |
) -> UniquifiedBy<Key> { | |
return UniquifiedBy(keyPath: keyPath, value: self) | |
} | |
public func uniquify<Key: Hashable>( | |
using closure: @escaping (Self) -> Key | |
) -> UniquifiedBy<Key> { | |
return UniquifiedBy(closure: closure, value: self) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment