Skip to content

Instantly share code, notes, and snippets.

@DevAndArtist
Last active February 21, 2019 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DevAndArtist/d8ed2b96cb11ff4a758571f176377275 to your computer and use it in GitHub Desktop.
Save DevAndArtist/d8ed2b96cb11ff4a758571f176377275 to your computer and use it in GitHub Desktop.
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