Swift - Stored Values Order Is Completely Changed in Dictionary

Swift - Stored values order is completely changed in Dictionary

This is because of the definition of Dictionaries:

Dictionary

A dictionary stores associations between keys of the same type and values of the same type in an collection with no defined ordering.

There is no order, they might come out differently than they were put in.
This is comparable to NSSet.


Edit:

NSDictionary

Dictionaries Collect Key-Value Pairs. Rather than simply maintaining an ordered or unordered collection of objects, an NSDictionary stores objects against given keys, which can then be used for retrieval.

There is also no order, however there is sorting on print for debugging purposes.

Dictionary [String: String] keys order changed when converted to array swift

Use plain dictionary as tableview datasource is bad idea.

However Dictionary can not be sorted. So it doesn't matter in what order you add your keys-values to the dictionary.

If you need sorted then use array of dictionary instead.

You should use models instead of plain dictionary that is easy to maintain :]

like

struct User {

var firstName:String?
var lastName:String?
var address:String?
var city:String?
}

Does a Swift Dictionary preserve same order if content is not changed regardless the number of runs?

As long as you don't modify a dictionary, the order of it's key/value pairs does not change. Everything else is unspecified. From the documentation (emphasis added):

The order of key-value pairs in a dictionary is stable between mutations but is otherwise unpredictable.

Note: This applies only to printing/enumerating the dictionary within a single program run. As @matt said, the order is randomized on each program run. And this is because the Hasher is randomized:

Do not save or otherwise reuse hash values across executions of your program. Hasher is usually randomly seeded, which means it will return different values on every new execution of your program. The hash algorithm implemented by Hasher may itself change between any two versions of the standard library.

Hash randomization was enforced in Swift 4.2, with the implementation of SE 0206 Hashable Enhancements:

To implement nondeterminism, Hasher uses an internal seed value initialized by the runtime during process startup. The seed is usually produced by a random number generator, but this may be disabled by defining the SWIFT_DETERMINISTIC_HASHING environment variable with a value of 1 prior to starting a Swift process.

Preserve order of dictionary items as declared in Swift?

In your case an array of custom objects might be more appropriate.
Here is a simple example that should help to get you started:

struct Unit : Printable {
let name: String
let factor: Double

// println() should print just the unit name:
var description: String { return name }
}


let units = [
Unit(name: "kg", factor: 1000.0),
Unit(name: "g", factor: 1.0),
Unit(name: "mg", factor: 0.001),
Unit(name: "lb", factor: 453.592292),
Unit(name: "oz", factor: 28.349523)
]

println(units) // [kg, g, mg, lb, oz]

(I am not sure if the non-metric unit factors are correct :)

How to maintain the order of the data in Dictionary in Swift?

Short answer:

You can not maintain order in the Dictionary.

Long answer:

Apple says:

Dictionaries are unordered collections of key-value associations.

Please refer this

To maintain the order you need to use the Array or create sorted Array(Ascending, Descending or by using specific key) from the Dictionary and use it.

Thank you.

Strange dictionary in swift

Becuase dictionaries have key pair values (a key being "1" and value being "!") it doesnt sort them in order like an array it will be different each time, (Where an array stores a value at a position that it was added) you can grab the value of a dictionary by knowing the key so it doesnt matter

Order changed for the parsed response when casting the response into a dictionary

Dictionary's don't maintain key order.

Let's just start with some basic data...

struct ShowData: CustomStringConvertible {
var id: String
var time: String

var description: String {
return "id = \(id); time = \(time)"
}
}

var rawData: [String: [ShowData]] = [:]

rawData["2018-04-20"] = [
ShowData(id: "WRK81ahlWcZZ", time: "17:00:00"),
ShowData(id: "cDqZjYKHP2xt", time: "20:00:00")]
rawData["2018-04-21"] = [
ShowData(id: "DcVQhohv9C3W", time: "17:00:00"),
ShowData(id: "bZOUk3AT6TMM", time: "20:00:00")]
rawData["2018-04-22"] = [
ShowData(id: "5YJAydTua6b2", time: "17:00:00"),
ShowData(id: "qKGXgWvV0r38", time: "20:00:00")]
rawData["2018-04-23"] = [
ShowData(id: "AGciEXINppMe", time: "17:00:00"),
ShowData(id: "YbKFBJV67hFW", time: "20:00:00")]
rawData["2018-04-24"] = [
ShowData(id: "5vj9t1i5B4J3", time: "17:00:00"),
ShowData(id: "um7UeNPJuAcv", time: "20:00:00")]
rawData["2018-04-25"] = [
ShowData(id: "AbS69J4SM4gG", time: "17:00:00"),
ShowData(id: "AbS69J4SM4gG", time: "20:00:00")]

for key in rawData.keys {
print(key)
}

For me, this prints out...

2018-04-25
2018-04-24
2018-04-23
2018-04-21
2018-04-20
2018-04-22

Now, you could look to finding a OrderedDictionary or you could Map the keys to an array and sort the array, which would give you vector back into the Dictionary

Or, you could sort the Entry data of the Dictionary...

let sorted = rawData.sorted { (lhs, rhs) -> Bool in
return lhs.key < rhs.key
}

for entry in sorted {
print(entry.key)
}

Which outputs...

2018-04-20
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25

Now, what's important to remember here is, this is nothing more then a String comparison, but you're dealing with dates. Personally, I hate dealing with String date/time values and as soon as I can, I convert to something which is actually practical for there purpose...

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd"

let actuallySortedByDate = rawData.sorted { (lhs, rhs) -> Bool in
return formatter.date(from: lhs.key)! < formatter.date(from: rhs.key)!
}

for entry in sorted {
print(entry.key)
}

Which outputs...

2018-04-20
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25

While this might not look any different from the first sorting, the comparisons performed are more actual to the data type (date objects).

Important

This is a demonstration of an idea. It's very important to understand that it's possible for the key to be invalid and the Date conversion to fail. Personally, I'd prefer to have the rawData keyed by Date, rather the String as it allows for an earlier detection of the issue. I've used a "forced unwrap" in the example purely for demonstration - you should validate the keys before hand and ensure that data is correct

Caveat: Mapping the keys in a Dictionary is actually a lot harder then it would seem to need to be. You can have a look at What's the cleanest way of applying map() to a dictionary in Swift? for more details. Needs less to say, use Swift 4!

Okay, so what this attempts to do, is convert the original [String: ShowData] Dictionary to a [Date: ShowData] Dictionary in a way which would allow you to deal with the possibility of invalid String keys.

enum ParserError: Error {
case invalidDateFormat
}

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd"
do {
let mapped = try Dictionary(uniqueKeysWithValues: rawData.map({ (entry) throws -> (Date, [ShowData]) in
guard let date = formatter.date(from: entry.key) else {
throw ParserError.invalidDateFormat
}
return (Calendar(identifier: .gregorian).startOfDay(for: date), entry.value)
}))

let actuallySortedByDate = mapped.sorted { (lhs, rhs) -> Bool in
return lhs.key < rhs.key
}

for entry in actuallySortedByDate {
print(entry.key)
}
} catch let error {
print(error)
}

And this eventually ends up printing:

2018-01-19 13:00:00 +0000
2018-01-20 13:00:00 +0000
2018-01-21 13:00:00 +0000
2018-01-22 13:00:00 +0000
2018-01-23 13:00:00 +0000
2018-01-24 13:00:00 +0000

ps: The idea is to try and take the time component out of the equation



Related Topics



Leave a reply



Submit