Last active
March 23, 2020 10:53
-
-
Save khanhldt/bbaa073485abbcf0dad1673ef0ad6bfe to your computer and use it in GitHub Desktop.
OrderedDictionary behaves like a Dictionary except that it maintains the insertion order of the keys, so iteration order matches insertion order.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by Khanh Le Do on 16/4/16. | |
// Copyright © 2016 kloc. All rights reserved. | |
// | |
import Foundation | |
/// Attribution to: | |
/// http://stackoverflow.com/questions/28633703/insertion-order-dictionary-like-javas-linkedhashmap-in-swift | |
/// With following updates: | |
/// - Fix syntax from Swift 1.0 to 2.0 | |
/// - Add method to sort the keys | |
/// - Make the class and methods public to be used in a library providing helper to other components. | |
// OrderedDictionary behaves like a Dictionary except that it maintains the insertion order of the keys, | |
// so iteration order matches insertion order. | |
public struct OrderedDictionary<KeyType:Hashable, ValueType> { | |
private var _dictionary:Dictionary<KeyType, ValueType> | |
private var _keys:Array<KeyType> | |
public init() { | |
_dictionary = [:] | |
_keys = [] | |
} | |
public init(minimumCapacity:Int) { | |
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity) | |
_keys = Array<KeyType>() | |
} | |
public init(_ dictionary:Dictionary<KeyType, ValueType>) { | |
_dictionary = dictionary | |
_keys = [KeyType](dictionary.keys) | |
} | |
public subscript(key:KeyType) -> ValueType? { | |
get { | |
return _dictionary[key] | |
} | |
set { | |
if newValue == nil { | |
self.removeValueForKey(key) | |
} | |
else { | |
self.updateValue(newValue!, forKey: key) | |
} | |
} | |
} | |
public mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? { | |
let oldValue = _dictionary.updateValue(value, forKey: key) | |
if oldValue == nil { | |
_keys.append(key) | |
} | |
return oldValue | |
} | |
public mutating func removeValueForKey(key:KeyType) { | |
_keys = _keys.filter { $0 != key } | |
_dictionary.removeValueForKey(key) | |
} | |
public mutating func removeAll(keepCapacity:Int) { | |
_keys = [] | |
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity) | |
} | |
public mutating func sortKeys(@noescape isOrderedBefore: (KeyType, KeyType) -> Bool) { | |
_keys.sortInPlace(isOrderedBefore) | |
} | |
public var count: Int { get { return _dictionary.count } } | |
// keys isn't lazy evaluated because it's just an array anyway | |
public var keys:[KeyType] { get { return _keys } } | |
// values is lazy evaluated because of the dictionary lookup and creating a new array | |
public var values:AnyGenerator<ValueType> { | |
get { | |
var index = 0 | |
return anyGenerator({ () -> ValueType? in | |
if index >= self._keys.count { | |
return nil | |
} | |
else { | |
let key = self._keys[index] | |
index++ | |
return self._dictionary[key] | |
} | |
}) | |
} | |
} | |
} | |
extension OrderedDictionary : SequenceType { | |
public func generate() -> AnyGenerator<(KeyType, ValueType)> { | |
var index = 0 | |
return anyGenerator({ () -> (KeyType, ValueType)? in | |
if index >= self._keys.count { | |
return nil | |
} | |
else { | |
let key = self._keys[index] | |
index++ | |
return (key, self._dictionary[key]!) | |
} | |
}) | |
} | |
} | |
public func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool { | |
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary | |
} | |
public func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool { | |
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary | |
} |
Interested in an updated Version for Swift 3 + an initializer to init it with an OrderedDictionary?
Leave a comment and let me know if this works out.
import Foundation
/// Attribution to:
/// http://stackoverflow.com/questions/28633703/insertion-order-dictionary-like-javas-linkedhashmap-in-swift
/// With following updates:
/// - Fix syntax from Swift 1.0 to 2.0 to 3.0
/// - Add method to sort the keys
/// - Make the class and methods public to be used in a library providing helper to other components.
// OrderedDictionary behaves like a Dictionary except that it maintains the insertion order of the keys,
// so iteration order matches insertion order.
public struct OrderedDictionary<KeyType:Hashable, ValueType> {
fileprivate var _dictionary:Dictionary<KeyType, ValueType>
fileprivate var _keys:Array<KeyType>
public init() {
_dictionary = [:]
_keys = []
}
public init(minimumCapacity:Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
_keys = Array<KeyType>()
}
public init(_ dictionary:Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = [KeyType](dictionary.keys)
}
public init(_ orderedDictionary: OrderedDictionary<KeyType, ValueType>){
_dictionary = orderedDictionary._dictionary
_keys = orderedDictionary._keys
}
public subscript(key:KeyType) -> ValueType? {
get {
return _dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key: key)
}
else {
self.updateValue(value: newValue!, forKey: key)
}
}
}
public mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
public mutating func removeValueForKey(key:KeyType) {
_keys = _keys.filter { $0 != key }
_dictionary.removeValue(forKey: key)
}
public mutating func removeAll(keepCapacity:Int) {
_keys = []
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
}
public mutating func sortKeys( isOrderedBefore: (KeyType, KeyType) -> Bool) {
_keys.sort(by: isOrderedBefore)
}
public var count: Int { get { return _dictionary.count } }
// keys isn't lazy evaluated because it's just an array anyway
public var keys:[KeyType] { get { return _keys } }
// values is lazy evaluated because of the dictionary lookup and creating a new array
public var values:AnyIterator<ValueType> {
get {
var index = 0
return AnyIterator({ () -> ValueType? in
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index += 1
return self._dictionary[key]
}
})
}
}
}
extension OrderedDictionary : Sequence {
public func makeIterator() -> AnyIterator<(KeyType, ValueType)> {
var index = 0
return AnyIterator({ () -> (KeyType, ValueType)? in
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index += 1
return (key, self._dictionary[key]!)
}
})
}
}
public func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
public func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
Interested in an updated Version for Swift 3 + an initializer to init it with an OrderedDictionary?
Leave a comment and let me know if this works out.
Thanks, this works like a charm.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice