Skip to content

Instantly share code, notes, and snippets.

@tkgstrator
Created August 2, 2023 08:08
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 tkgstrator/12e6c3fec8addcb8153048a237bd1e01 to your computer and use it in GitHub Desktop.
Save tkgstrator/12e6c3fec8addcb8153048a237bd1e01 to your computer and use it in GitHub Desktop.
//
// OrdableDictionary.swift
// OrdableDictionary
//
// Created by tkgstrator on 2023/08/02.
// Copyright © 2023 Magi Corporation. All rights reserved.
//
@_implementationOnly import Collections
import Foundation
/// A wrapper for a dictionary to be en/decoded as expected.
/// Videlicet, this is yet another workaround for [SR-7788](https://bugs.swift.org/browse/SR-7788).
internal struct OrdableDictionary<Key, Value> where Key: OrdableDictionaryKey, Value: Codable {
/// Wrapped dictionary.
var dictionary: OrderedDictionary<Key, Value>
private init(_dictionary: OrderedDictionary<Key, Value>) {
self.dictionary = _dictionary
}
/// Creates an empty dictionary.
init() {
self.init(_dictionary: [:])
}
/// Creates an empty dictionary with preallocated space
/// for at least the specified number of elements.
init(minimumCapacity: Int) {
self.init(_dictionary: .init(minimumCapacity: minimumCapacity))
}
/// Creates a new dictionary from the key-value pairs in the given sequence.
init<S>(uniqueKeysWithValues keysAndValues: S) where S: Sequence, S.Element == (Key, Value) {
let value = keysAndValues.reduce(into: OrdableDictionary<Key, Value>(), { $0[$1.0] = $1.1 })
print(value)
self = value
}
/// 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.
init<S>(_ keysAndValues: S,
uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows where S: Sequence, S.Element == (Key, Value) {
self.init(_dictionary: try OrderedDictionary(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.
init<S>(grouping values: S,
by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S: Sequence {
self.init(_dictionary: try OrderedDictionary(grouping: values, by: keyForValue))
}
var isEmpty: Bool {
self.dictionary.isEmpty
}
var count: Int {
self.dictionary.count
}
var underestimatedCount: Int {
self.dictionary.underestimatedCount
}
// var capacity: Int {
// self.dictionary.capacity
// }
subscript(key: Key) -> Value? {
get {
self.dictionary[key]
}
set {
self.dictionary[key] = newValue
}
}
subscript(key: Key, default defaultValue: @autoclosure () -> Value) -> Value {
get {
self.dictionary[key, default: defaultValue()]
}
set {
self.dictionary[key, default: defaultValue()] = newValue
}
}
}
extension OrdableDictionary: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
for (key, value) in self.dictionary.shuffled() {
print(key, value)
try container.encode(value, forKey: key)
}
}
}
extension OrdableDictionary: Decodable {
init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: Key.self)
for key in container.allKeys {
self[key] = try container.decode(Value.self, forKey: key)
}
}
}
extension OrdableDictionary: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (Key, Value)...) {
self.init()
for (key, value) in elements {
self.dictionary[key] = value
}
}
}
extension OrdableDictionary: Sequence {
func makeIterator() -> OrderedDictionary<Key, Value>.Iterator {
self.dictionary.makeIterator()
}
}
// extension OrdableDictionary: Sequence {
// typealias Element = OrderedDictionary<Key, Value>.Element
//
// typealias Iterator = DictionaryIterator<Key, Value>
//
// func makeIterator() -> Iterator {
// self.dictionary.makeIterator()
// }
// }
// extension OrdableDictionary: Collection {
// typealias Index = DictionaryIndex<Key, Value>
//
// var startIndex: Index {
// self.dictionary.startIndex
// }
//
// var endIndex: Index {
// self.dictionary.endIndex
// }
//
// subscript(position: Index) -> (key: Key, value: Value) {
// self.dictionary[position]
// }
//
// func index(after i: Index) -> Index {
// self.dictionary.index(after: i)
// }
// }
extension OrdableDictionary: CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable {
var description: String {
self.dictionary.description
}
var debugDescription: String {
self.dictionary.debugDescription
}
var customMirror: Mirror {
self.dictionary.customMirror
}
}
extension OrdableDictionary: Equatable where Value: Equatable {}
extension OrdableDictionary: Hashable where Value: Hashable {}
// MARK: - Other Methods
extension OrdableDictionary {
// typealias Keys = OrderedDictionary<Key, Value>.Keys
typealias Values = OrderedDictionary<Key, Value>.Values
//
// func index(forKey key: Key) -> Index? {
// self.dictionary.index(forKey: key)
// }
//
// var keys: Keys {
// self.dictionary.keys
// }
//
var values: Values {
self.dictionary.values
}
//
// var first: Element? {
// self.dictionary.first
// }
//
// /// Returns a random element of the collection.
// func randomElement() -> (key: Key, value: Value)? {
// self.dictionary.randomElement()
// }
//
// /// Returns a random element of the collection, using the given generator as a source for randomness.
// func randomElement<T>(using generator: inout T) -> (key: Key, value: Value)? where T: RandomNumberGenerator {
// self.dictionary.randomElement(using: &generator)
// }
//
// /// Updates the value stored in the dictionary for the given key,
// /// or adds a new key-value pair if the key does not exist.
// @discardableResult
// mutating func updateValue(_ value: Value, forKey key: Key) -> Value? {
// self.dictionary.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.
// mutating func merge<S>(_ other: S,
// uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows where S: Sequence, S.Element == (Key, Value) {
// try self.dictionary.merge(other, uniquingKeysWith: combine)
// }
//
// /// Creates a codable dictionary by merging key-value pairs in a sequence into the dictionary,
// /// using a combining closure to determine the value for duplicate keys.
// func merging<S>(_ other: S,
// uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows -> OrdableDictionary<Key, Value> where S: Sequence, S.Element == (Key, Value) {
// OrdableDictionary(_dictionary: try self.dictionary.merging(other, uniquingKeysWith: combine))
// }
//
// mutating func reserveCapacity(_ minimumCapacity: Int) {
// self.dictionary.reserveCapacity(minimumCapacity)
// }
//
// @discardableResult
// mutating func removeValue(forKey key: Key) -> Value? {
// self.dictionary.removeValue(forKey: key)
// }
//
// @discardableResult
// mutating func remove(at index: Index) -> Element {
// self.dictionary.remove(at: index)
// }
//
// mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
// self.dictionary.removeAll(keepingCapacity: keepCapacity)
// }
//
// /// Returns a new codable dictionary containing the keys of this dictionary
// /// with the values transformed by the given closure.
// func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> OrdableDictionary<Key, T> {
// OrdableDictionary<Key, T>(_dictionary: try self.dictionary.mapValues(transform))
// }
}
//
// OrdableDictionaryKey.swift
// OrdableDictionary
//
// Created by tkgstrator on 2023/08/02.
// Copyright © 2023 Magi Corporation. All rights reserved.
//
import Foundation
/// A type that can be a key of `OrdableDictionary`.
public protocol OrdableDictionaryKey: Hashable, CodingKey {}
// Note: `extension OrdableDictionaryKey where Self: LosslessStringConvertible` will cause ambiguity errors.
extension OrdableDictionaryKey where Self: FixedWidthInteger {
public var description: String {
// Workaround for https://github.com/YOCKOW/SwiftOrdableDictionary/issues/2
func __description<B>(_ int: B) -> String where B: BinaryInteger {
int.description
}
return __description(self)
}
public var stringValue: String {
String(self, radix: 10)
}
public var intValue: Int? {
guard Int.min <= self && self <= Int.max else { return nil }
return Int(self)
}
public init?(stringValue: String) {
self.init(stringValue)
}
public init?(intValue: Int) {
guard Self.min <= intValue && intValue <= Self.max else { return nil }
self.init(intValue)
}
}
extension OrdableDictionaryKey where Self: RawRepresentable, Self.RawValue: OrdableDictionaryKey {
public var stringValue: String {
self.rawValue.stringValue
}
public var intValue: Int? {
self.rawValue.intValue
}
public init?(stringValue: String) {
guard let rawValue = Self.RawValue(stringValue: stringValue) else { return nil }
self.init(rawValue: rawValue)
}
public init?(intValue: Int) {
guard let rawValue = Self.RawValue(intValue: intValue) else { return nil }
self.init(rawValue: rawValue)
}
}
// Requires this because of https://github.com/apple/swift/blob/master/lib/Sema/DerivedConformanceCodingKey.cpp
extension OrdableDictionaryKey where Self: RawRepresentable, Self.RawValue == Int {
public var stringValue: String {
String(self.rawValue)
}
public var intValue: Int? {
self.rawValue
}
public init?(stringValue: String) {
guard let integer = Int(stringValue) else { return nil }
self.init(rawValue: integer)
}
public init?(intValue: Int) {
self.init(rawValue: intValue)
}
}
extension String: OrdableDictionaryKey {
public var stringValue: String {
self
}
public var intValue: Int? {
Int(self)
}
public init?(stringValue: String) {
self = stringValue
}
public init?(intValue: Int) {
self.init(intValue)
}
}
extension Substring: OrdableDictionaryKey {
public var stringValue: String {
String(self)
}
public var intValue: Int? {
Int(self)
}
public init?(stringValue: String) {
self = stringValue[...]
}
public init?(intValue: Int) {
self.init(stringValue: String(intValue))
}
}
extension Unicode.Scalar: OrdableDictionaryKey {
public var stringValue: String {
String(self)
}
public var intValue: Int? {
Int(self.stringValue)
}
public init?(stringValue: String) {
// Complexity of `count` of `UnicodeScalarView` is O(n)
let scalars = stringValue.unicodeScalars
if scalars.isEmpty { return nil }
guard scalars.dropFirst().isEmpty else { return nil }
self = scalars.first!
}
public init?(intValue: Int) {
self.init(stringValue: String(intValue))
}
}
extension Character: OrdableDictionaryKey {
public var stringValue: String {
String(self)
}
public var intValue: Int? {
Int(self.stringValue)
}
public init?(stringValue: String) {
// Complexity of `count` of `String` is O(n)
if stringValue.isEmpty { return nil }
guard stringValue.dropFirst().isEmpty else { return nil }
self = stringValue.first!
}
public init?(intValue: Int) {
self.init(stringValue: String(intValue))
}
}
extension Int: OrdableDictionaryKey {
public var intValue: Int? {
self
}
public init?(intValue: Int) {
self = intValue
}
}
extension Int8: OrdableDictionaryKey {}
extension Int16: OrdableDictionaryKey {}
extension Int32: OrdableDictionaryKey {}
extension Int64: OrdableDictionaryKey {}
extension UInt8: OrdableDictionaryKey {}
extension UInt16: OrdableDictionaryKey {}
extension UInt32: OrdableDictionaryKey {}
extension UInt64: OrdableDictionaryKey {}
extension UInt: OrdableDictionaryKey {}
extension Float: OrdableDictionaryKey {
public var stringValue: String {
self.description
}
public var intValue: Int? {
Int(self)
}
public init?(stringValue: String) {
self.init(stringValue)
}
public init?(intValue: Int) {
self.init(intValue)
}
}
extension Double: OrdableDictionaryKey {
public var stringValue: String {
self.description
}
public var intValue: Int? {
Int(self)
}
public init?(stringValue: String) {
self.init(stringValue)
}
public init?(intValue: Int) {
self.init(intValue)
}
}
#if arch(i386) || arch(x86_64)
extension Float80: OrdableDictionaryKey {
public var stringValue: String {
self.description
}
public var intValue: Int? {
Int(self)
}
public init?(stringValue: String) {
self.init(stringValue)
}
public init?(intValue: Int) {
self.init(intValue)
}
}
#endif
extension Bool: OrdableDictionaryKey {
public var stringValue: String {
self ? "true" : "false"
}
public var intValue: Int? {
self ? 1 : 0
}
public init?(stringValue: String) {
// Also supports YAML's Boolean: https://yaml.org/type/bool.html
switch stringValue.lowercased() {
case "y", "yes", "true", "on":
self = true
case "n", "no", "false", "off":
self = false
default:
return nil
}
}
public init?(intValue: Int) {
self = intValue != 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment