TM-SGNL-iOS/SignalServiceKit/Util/OrderedDictionary.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

161 lines
4.8 KiB
Swift

//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public struct OrderedDictionary<KeyType: Hashable, ValueType> {
private var keyValueMap = [KeyType: ValueType]()
public private(set) var orderedKeys = [KeyType]()
public init() { }
public init(keyValueMap: [KeyType: ValueType], orderedKeys: [KeyType]) {
owsAssertDebug(keyValueMap.count == orderedKeys.count)
owsAssertDebug(Set(orderedKeys) == Set(keyValueMap.keys), "Invalid contents.")
self.keyValueMap = keyValueMap
self.orderedKeys = orderedKeys
}
public subscript(key: KeyType) -> ValueType? {
return keyValueMap[key]
}
public func hasValue(forKey key: KeyType) -> Bool {
return keyValueMap[key] != nil
}
public mutating func insert(key: KeyType, at index: Int, value: ValueType) {
owsPrecondition(keyValueMap[key] == nil, "Key already in dictionary: \(key)")
owsAssertDebug(!orderedKeys.contains(key), "Unexpected duplicate key in key list: \(key)")
keyValueMap[key] = value
orderedKeys.insert(key, at: index)
owsAssertDebug(orderedKeys.count == keyValueMap.count, "Invalid contents.")
}
public mutating func append(key: KeyType, value: ValueType) {
insert(key: key, at: count, value: value)
}
public mutating func prepend(key: KeyType, value: ValueType) {
insert(key: key, at: 0, value: value)
}
@discardableResult
public mutating func replace(key: KeyType, value: ValueType) -> ValueType {
guard let oldValue = keyValueMap.updateValue(value, forKey: key) else {
owsFail("Key is not present in OrderedDictionary: \(key)")
}
owsAssertDebug(orderedKeys.contains(key), "Missing key in key list: \(key)")
owsAssertDebug(orderedKeys.count == keyValueMap.count, "Invalid contents.")
return oldValue
}
@discardableResult
public mutating func remove(key: KeyType) -> ValueType? {
guard let value = keyValueMap.removeValue(forKey: key) else {
return nil
}
owsAssertDebug(orderedKeys.contains(key), "Missing key in key list: \(key)")
orderedKeys.removeAll { $0 == key }
owsAssertDebug(orderedKeys.count == keyValueMap.count, "Invalid contents.")
return value
}
public mutating func remove(at index: Int) {
let key = orderedKeys[index]
guard keyValueMap.removeValue(forKey: key) != nil else {
owsFailDebug("Missing key in dictionary: \(key)")
return
}
orderedKeys.remove(at: index)
}
public mutating func removeSubrange<R: RangeExpression>(_ range: R) where R.Bound == Int {
orderedKeys[range].forEach { key in
guard keyValueMap.removeValue(forKey: key) != nil else {
owsFailDebug("Missing key in dictionary: \(key)")
return
}
}
orderedKeys.removeSubrange(range)
}
public mutating func removeAll() {
keyValueMap.removeAll()
orderedKeys.removeAll()
}
public mutating func moveExistingKeyToFirst(_ key: KeyType) {
guard let index = orderedKeys.firstIndex(of: key) else {
owsFail("Key not in dictionary: \(key)")
}
orderedKeys.remove(at: index)
orderedKeys.insert(key, at: 0)
}
public var orderedValues: [ValueType] {
return self.map { $0.value }
}
public var firstKey: KeyType? {
orderedKeys.first
}
public var lastKey: KeyType? {
orderedKeys.last
}
}
// MARK: -
extension OrderedDictionary: RandomAccessCollection {
public var startIndex: Int { 0 }
public var endIndex: Int { self.orderedKeys.count }
public subscript(position: Int) -> (key: KeyType, value: ValueType) {
owsPrecondition(indices.contains(position))
let key = orderedKeys[position]
guard let value = keyValueMap[key] else {
owsFail("Missing value")
}
return (key: key, value: value)
}
}
// MARK: -
extension OrderedDictionary: Encodable where KeyType: Encodable, ValueType: Encodable {}
extension OrderedDictionary: Decodable where KeyType: Decodable, ValueType: Decodable {}
// MARK: - Sequence
extension OrderedDictionary: Sequence {
public typealias IteratorTuple = (key: KeyType, value: ValueType)
public typealias Iterator = AnyIterator<IteratorTuple>
public func makeIterator() -> Iterator {
let keyValueMap = self.keyValueMap
var keyIterator = orderedKeys.makeIterator()
return Iterator { () -> IteratorTuple? in
guard let key = keyIterator.next() else {
return nil
}
guard let value = keyValueMap[key] else {
return nil
}
return (key: key, value: value)
}
}
}