Skip to content

Instantly share code, notes, and snippets.

@dabrahams
Created May 15, 2020 01:59
Show Gist options
  • Select an option

  • Save dabrahams/b3540deac52bc541031d800f633e24b8 to your computer and use it in GitHub Desktop.

Select an option

Save dabrahams/b3540deac52bc541031d800f633e24b8 to your computer and use it in GitHub Desktop.
Plain Swift Dictionary uses a tuple as its Element type, which prevents the Element from conforming to anything
/// A Dictionary with a nominal `Element` type, that can conform to things.
@frozen public struct Dictionary2<Key: Hashable, Value> {
public typealias Base = [Key : Value]
/// A view of a dictionary's keys.
public typealias Keys = Base.Keys
/// A view of a dictionary's values.
public typealias Values = Base.Values
/// The position of a key-value pair in a dictionary.
public typealias Index = Base.Index
/// The underlying Swift Dictionary
public var base: Base
/// Creates an instance equivalent to `base`.
@inlinable public init(_ base: Base) {
self.base = base
}
/// The element type of a dictionary, just like a tuple containing an
/// individual key-value pair, but nominal.
@frozen public struct Element {
public var key: Key
public var value: Value
@inlinable public init(key: Key, value: Value) {
(self.key, self.value) = (key, value)
}
@inlinable
internal init(tuple x: (key: Key, value: Value)) {
(self.key, self.value) = (x.key, x.value)
}
internal var tuple: (key: Key, value: Value) { (key, value) }
}
/// Creates an empty dictionary.
@inlinable public init() { base = .init() }
/// Creates an empty dictionary with preallocated space for at least the
/// specified number of elements.
public init(minimumCapacity: Int) {
base = .init(minimumCapacity: minimumCapacity)
}
/// Creates a new dictionary from the key-value pairs in the given sequence.
@inlinable public init<S>(
uniqueKeysWithValues keysAndValues: S)
where S : Sequence, S.Element == (Key, Value)
{
base = .init(uniqueKeysWithValues: keysAndValues)
}
/// Creates a new dictionary from the key-value pairs in the given sequence,
/// using a combining closure to determine the value for any duplicate keys.
@inlinable public init<S>(
_ keysAndValues: S,
uniquingKeysWith combine: (Value, Value) throws -> Value
) rethrows where S : Sequence, S.Element == (Key, Value) {
try base = .init(keysAndValues, uniquingKeysWith: combine)
}
/// Creates a new dictionary whose keys are the groupings returned by the
/// given closure and whose values are arrays of the elements that returned
/// each key.
@inlinable public init<S>(
grouping values: S, by keyForValue: (S.Element) throws -> Key)
rethrows where Value == [S.Element], S : Sequence
{
try base = .init(grouping: values, by: keyForValue)
}
/// Returns a new dictionary containing the key-value pairs of the dictionary
/// that satisfy the given predicate.
@available(swift 4.0)
@inlinable public func filter(
_ isIncluded: (Dictionary<Key, Value>.Element) throws -> Bool
) rethrows -> Dictionary2 {
try .init(base.filter(isIncluded))
}
/// Accesses the value associated with the given key for reading and writing.
@inlinable public subscript(key: Key) -> Value? {
base[key]
}
/// Accesses the value with the given key. If the dictionary doesn't contain
/// the given key, accesses the provided default value as if the key and
/// default value existed in the dictionary.
@inlinable public subscript(
key: Key, default defaultValue: @autoclosure () -> Value
) -> Value {
base[key, default: defaultValue()]
}
/// Returns a new dictionary containing the keys of this dictionary with the
/// values transformed by the given closure.
@inlinable public func mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> [Key : T] {
try base.mapValues(transform)
}
/// Returns a new dictionary containing only the key-value pairs that have
/// non-`nil` values as the result of transformation by the given closure.
@inlinable public func compactMapValues<T>(
_ transform: (Value) throws -> T?
) rethrows -> [Key : T] { try base.compactMapValues(transform) }
/// Updates the value stored in the dictionary for the given key, or adds a
/// new key-value pair if the key does not exist.
@inlinable public mutating func updateValue(
_ value: Value, forKey key: Key
) -> Value? {
base.updateValue(value, forKey: key)
}
/// Merges the key-value pairs in the given sequence into the dictionary,
/// using a combining closure to determine the value for any duplicate keys.
@inlinable public mutating func merge<S>(
_ other: S,
uniquingKeysWith combine: (Value, Value) throws -> Value
) rethrows where S : Sequence, S.Element == (Key, Value)
{
try base.merge(other, uniquingKeysWith: combine)
}
/// Merges the given dictionary into this dictionary, using a combining
/// closure to determine the value for any duplicate keys.
@inlinable public mutating func merge(
_ other: Self,
uniquingKeysWith combine: (Value, Value) throws -> Value
) rethrows {
try base.merge(other.base, uniquingKeysWith: combine)
}
/// Creates a dictionary by merging key-value pairs in a sequence into the
/// dictionary, using a combining closure to determine the value for
/// duplicate keys.
@inlinable public func merging<S>(
_ other: S, uniquingKeysWith combine: (Value, Value) throws -> Value
) rethrows -> Self where S : Sequence, S.Element == (Key, Value) {
try .init(base.merging(other, uniquingKeysWith: combine))
}
/// Creates a dictionary by merging the given dictionary into this
/// dictionary, using a combining closure to determine the value for
/// duplicate keys.
@inlinable public func merging(
_ other: Self, uniquingKeysWith combine: (Value, Value) throws -> Value
) rethrows -> Self {
try .init(base.merging(other.base, uniquingKeysWith: combine))
}
/// Removes and returns the key-value pair at the specified index.
@inlinable public mutating func remove(at index: Index) -> Element {
.init(tuple: base.remove(at: index))
}
/// Removes the given key and its associated value from the dictionary.
@inlinable public mutating func removeValue(forKey key: Key) -> Value? {
base.removeValue(forKey: key)
}
/// Removes all key-value pairs from the dictionary.
@inlinable public mutating func removeAll(
keepingCapacity keepCapacity: Bool = false
) {
base.removeAll(keepingCapacity: keepCapacity)
}
/// A collection containing just the keys of the dictionary.
@available(swift 4.0)
@inlinable public var keys: Keys { base.keys }
/// A collection containing just the values of the dictionary.
@available(swift 4.0)
@inlinable public var values: Values { base.values }
/// An iterator over the members of a `Dictionary2<Key, Value>`.
@frozen public struct Iterator : IteratorProtocol {
@usableFromInline
internal typealias Base = Swift.Dictionary<Key,Value>.Iterator
@usableFromInline
init(base: Base) { self.base = base }
@usableFromInline
var base: Base
@inlinable
public mutating func next() -> Element? {
return base.next().map(Element.init(tuple:))
}
}
/// Removes and returns the first key-value pair of the dictionary if the
/// dictionary isn't empty.
@inlinable public mutating func popFirst() -> Element? {
base.popFirst().map(Element.init(tuple:))
}
/// The total number of key-value pairs that the dictionary can contain without
/// allocating new storage.
@inlinable public var capacity: Int { base.capacity }
/// Reserves enough space to store the specified number of key-value pairs.
public mutating func reserveCapacity(_ minimumCapacity: Int) {
base.reserveCapacity(minimumCapacity)
}
}
extension Dictionary2 : Collection {
/// The position of the first element in a nonempty dictionary.
@inlinable public var startIndex: Index { base.startIndex }
/// The dictionary's "past the end" position---that is, the position one
/// greater than the last valid subscript argument.
@inlinable public var endIndex: Index { base.endIndex }
/// Returns the position immediately after the given index.
@inlinable public func index(after i: Index) -> Index {
base.index(after: i)
}
/// Replaces the given index with its successor.
@inlinable public func formIndex(after i: inout Index) {
base.formIndex(after: &i)
}
/// Returns the index for the given key.
@inlinable public func index(forKey key: Key) -> Index? {
base.index(forKey: key)
}
/// Accesses the key-value pair at the specified position.
@inlinable public subscript(position: Index) -> Element {
.init(tuple: base[position])
}
/// The number of key-value pairs in the dictionary.
@inlinable public var count: Int { base.count }
/// A Boolean value that indicates whether the dictionary is empty.
@inlinable public var isEmpty: Bool { base.isEmpty }
}
extension Dictionary2 : Sequence {
/// Returns an iterator over the dictionary's key-value pairs.
@inlinable public func makeIterator() -> Iterator {
return .init(base: base.makeIterator())
}
}
extension Dictionary2 : CustomReflectable {
/// A mirror that reflects the dictionary.
public var customMirror: Mirror { base.customMirror }
}
/*
extension Dictionary2 : KeyPathIterable {
/// A type that can represent a collection of all key paths of this type.
public typealias AllKeyPaths = [PartialKeyPath<Dictionary2>]
/// A collection of all custom key paths of this value.
public var allKeyPaths: [PartialKeyPath<Dictionary2>] { base.allKeyPaths }
}
*/
extension Dictionary2 : CustomStringConvertible, CustomDebugStringConvertible {
/// A string that represents the contents of the dictionary.
public var description: String { base.description }
/// A string that represents the contents of the dictionary, suitable for
/// debugging.
public var debugDescription: String { base.debugDescription }
}
extension Dictionary2 : Hashable where Value : Hashable {}
extension Dictionary2 : Equatable where Value : Equatable {}
extension Dictionary2 : Decodable where Key : Decodable, Value : Decodable {
/// Creates a new dictionary by decoding from the given decoder.
public init(from decoder: Decoder) throws {
try base = .init(from: decoder)
}
}
extension Dictionary2 : Encodable where Key : Encodable, Value : Encodable {
/// Encodes the contents of this dictionary into the given encoder.
public func encode(to encoder: Encoder) throws {
try base.encode(to: encoder)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment