Insertion-Order Dictionary (like Java's LinkedHashMap) in Swift?
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
}
Ordered map in Swift
You can order them by having keys with type Int
.
var myDictionary: [Int: [String: String]]?
or
var myDictionary: [Int: (String, String)]?
I recommend the first one since it is a more common format (JSON for example).
Create a Swift Dictionary subclass?
Swift dictionaries are structs, not classes, so they cannot be subclassed. Ideally, the methods you're working with would be declared to take an appropriately constrained generic CollectionType (or ExtensibleCollectionType, or SequenceType, depending on the situation), rather than specifically a Dictionary.
If that doesn't work for you for whatever reason, you could subclass NSDictionary instead.
(edit) and as Antonio points out, you can do extension Dictionary { … } to add things to the Dictionary struct, which can replace subclassing in some cases.
How to effectively process HashMap in order described by list?
Something like this should be equivalent, using Stream.reduce
:
eventsOrder.stream() // "event1", "event2", "event3", "event4", "event5"
.map(eventsData::get) // "data1", "data2", "data3", null, null
.filter(Objects::nonNull) // "data1", "data2", "data3"
.reduce(YourClass::merge) // merge(merge("data1", "data2"), "data3")
.get() // gets the result from the Optional
The Optional.get
method throws NoSuchElementException
if there are no events with data; your original code's namesToProcess.get(0)
will throw IndexOutOfBoundsException
in that case. That should be the only difference in behaviour, assuming your eventsData
map doesn't have any null
values.
How to sort hashtable/enumeration based on original input
You should be using LinkedHashMap<>
, which combines a hash map for quick access but also keeps the elements ordered by their insertion order. For example
Map<String,String> dict = new LinkedHashMap<>();
dict.put("Emma", "Watson");
dict.put("Tom", "Hanks");
dict.put("Keanu", "Reeves");
for (String s : dict.keySet())
System.out.println(s);
This will output the keys in the order in which they were inserted into the map.
BTW, Dictionary
, Hashtable
and related classes are very old and are superseded by Map
and its implementations.
SwiftyJSON order of elements inside JSON object
Dictionaries are, by design, not stable on insertion order (insertion order doesn't matter when iterating keys) EF is arguably broken in requiring order of JS objects, which also isn't specified. That said, you might be able to solve the problem using something like the answers to this question: Insertion-Order Dictionary (like Java's LinkedHashMap) in Swift?. Barring that, you'll need to customize your serialization operation (whatever turns your Dictionary into JSON) to special case this.
What is the equivalent of a Java HashMap String,Integer in Swift
I believe you can use a dictionary. Here are two ways to do the dictionary part.
var someProtocol = [String : Int]()
someProtocol["one"] = 1
someProtocol["two"] = 2
or try this which uses type inference
var someProtocol = [
"one" : 1,
"two" : 2
]
as for the for loop
var index: Int
for (e, value) in someProtocol {
index = value
}
How do I get the key at a specific index from a Dictionary in Swift?
That's because keys
returns LazyMapCollection<[Key : Value], Key>
, which can't be subscripted with an Int
. One way to handle this is to advance the dictionary's startIndex
by the integer that you wanted to subscript by, for example:
let intIndex = 1 // where intIndex < myDictionary.count
let index = myDictionary.index(myDictionary.startIndex, offsetBy: intIndex)
myDictionary.keys[index]
Another possible solution would be to initialize an array with keys
as input, then you can use integer subscripts on the result:
let firstKey = Array(myDictionary.keys)[0] // or .first
Remember, dictionaries are inherently unordered, so don't expect the key at a given index to always be the same.
Related Topics
Can You Give Uistackview Borders
Background Animation with Depth in Spritekit
Swift - How to Create a View with a Shape Cropped in It
Swift: Extract Float from Byte Data
Skip Item When Performing Map in Swift
Macos/Swift Capture Audio with Avcapturesession
Xcode Incorrectly Reporting Swift Access Race Condition
What Is the Backslash(\) Used for in Swiftui
Changing Value in Nested Dictionary in Swift
How to Edit the Uiblureffect Intensity
Swift Anyobject Is Not Convertible to String/Int
Get Location from Center of Screen Swift Mapkit
Is There Any Way of Locking an Object in Swift Like in C#
How to Properly Implement the Equatable Protocol in a Class Hierarchy