Insertion-Order Dictionary (like Java's LinkedHashMap) in Swift?

前端 未结 2 878
隐瞒了意图╮
隐瞒了意图╮ 2020-12-31 02:55

Is there a standard swift class that is a Dictionary, but keeps keys in insertion-order like Java\'s LinkedHashMap? If not, how would one be implemented?

相关标签:
2条回答
  • 2020-12-31 03:15

    Swift 5 version:

    // OrderedDictionary behaves like a Dictionary except that it maintains
    //  the insertion order of the keys, so iteration order matches insertion
    //  order.
    struct OrderedDictionary<KeyType: Hashable, ValueType> {
        private var _dictionary: Dictionary<KeyType, ValueType>
        private var _keys: Array<KeyType>
    
        init() {
            _dictionary = [:]
            _keys = []
        }
    
        init(minimumCapacity: Int) {
            _dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: minimumCapacity)
            _keys = Array<KeyType>()
        }
    
        init(_ dictionary: Dictionary<KeyType, ValueType>) {
            _dictionary = dictionary
            _keys = dictionary.keys.map { $0 }
        }
    
        subscript(key: KeyType) -> ValueType? {
            get {
                _dictionary[key]
            }
            set {
                if newValue == nil {
                    self.removeValueForKey(key: key)
                } else {
                    _ = self.updateValue(value: newValue!, forKey: key)
                }
            }
        }
    
        mutating func updateValue(value: ValueType, forKey key: KeyType) -> ValueType? {
            let oldValue = _dictionary.updateValue(value, forKey: key)
            if oldValue == nil {
                _keys.append(key)
            }
            return oldValue
        }
    
        mutating func removeValueForKey(key: KeyType) {
            _keys = _keys.filter {
                $0 != key
            }
            _dictionary.removeValue(forKey: key)
        }
    
        mutating func removeAll(keepCapacity: Int) {
            _keys = []
            _dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: keepCapacity)
        }
    
        var count: Int {
            get {
                _dictionary.count
            }
        }
    
        // keys isn't lazy evaluated because it's just an array anyway
        var keys: [KeyType] {
            get {
                _keys
            }
        }
    
        var values: Array<ValueType> {
            get {
                _keys.map { _dictionary[$0]! }
            }
        }
    
        static func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
            lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
        }
    
        static func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
            lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
        }
    
    }
    
    extension OrderedDictionary: Sequence {
    
        public func makeIterator() -> OrderedDictionaryIterator<KeyType, ValueType> {
            OrderedDictionaryIterator<KeyType, ValueType>(sequence: _dictionary, keys: _keys, current: 0)
        }
    
    }
    
    struct OrderedDictionaryIterator<KeyType: Hashable, ValueType>: IteratorProtocol {
        let sequence: Dictionary<KeyType, ValueType>
        let keys: Array<KeyType>
        var current = 0
    
        mutating func next() -> (KeyType, ValueType)? {
            defer { current += 1 }
            guard sequence.count > current else {
                return nil
            }
    
            let key = keys[current]
            guard let value = sequence[key] else {
                return nil
            }
            return (key, value)
        }
    
    }
    

    I didn't found way to make values 'lazy'.. need more research

    0 讨论(0)
  • 2020-12-31 03:25

    Didn't know of one and it was an interesting problem to solve (already put it in my standard library of stuff) Mostly it's just a matter of maintaining a dictionary and an array of the keys side-by-side. But standard operations like for (key, value) in od and for key in od.keys will iterate in insertion order rather than a semi random fashion.

    // OrderedDictionary behaves like a Dictionary except that it maintains
    //  the insertion order of the keys, so iteration order matches insertion
    //  order.
    struct OrderedDictionary<KeyType:Hashable, ValueType> {
        private var _dictionary:Dictionary<KeyType, ValueType>
        private var _keys:Array<KeyType>
    
        init() {
            _dictionary = [:]
            _keys = []
        }
    
        init(minimumCapacity:Int) {
            _dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
            _keys = Array<KeyType>()
        }
    
        init(_ dictionary:Dictionary<KeyType, ValueType>) {
            _dictionary = dictionary
            _keys = map(dictionary.keys) { $0 }
        }
    
        subscript(key:KeyType) -> ValueType? {
            get {
                return _dictionary[key]
            }
            set {
                if newValue == nil {
                    self.removeValueForKey(key)
                }
                else {
                    self.updateValue(newValue!, forKey: key)
                }
            }
        }
    
        mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
            let oldValue = _dictionary.updateValue(value, forKey: key)
            if oldValue == nil {
                _keys.append(key)
            }
            return oldValue
        }
    
        mutating func removeValueForKey(key:KeyType) {
            _keys = _keys.filter { $0 != key }
            _dictionary.removeValueForKey(key)
        }
    
        mutating func removeAll(keepCapacity:Int) {
            _keys = []
            _dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
        }
    
        var count: Int { get { return _dictionary.count } }
    
        // keys isn't lazy evaluated because it's just an array anyway
        var keys:[KeyType] { get { return _keys } }
    
        // values is lazy evaluated because of the dictionary lookup and creating a new array
        var values:GeneratorOf<ValueType> {
            get {
                var index = 0
                return GeneratorOf<ValueType> {
                    if index >= self._keys.count {
                        return nil
                    }
                    else {
                        let key = self._keys[index]
                        index++
                        return self._dictionary[key]
                    }
                }
            }
        }
    }
    
    extension OrderedDictionary : SequenceType {
        func generate() -> GeneratorOf<(KeyType, ValueType)> {
            var index = 0
            return GeneratorOf<(KeyType, ValueType)> {
                if index >= self._keys.count {
                    return nil
                }
                else {
                    let key = self._keys[index]
                    index++
                    return (key, self._dictionary[key]!)
                }
            }
        }
    }
    
    func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
        return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
    }
    
    func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
        return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
    }
    
    0 讨论(0)
提交回复
热议问题