5

I'm learning swift and trying to understand dictionaries. I'm used to PHP, where you might write the following...

$dataPoints = Array("A"=>1,"B"=>2,"C"=>3);
foreach($dataPoints as $value) {
    echo($value);
}

In this example, the values would be output in order - 1, 2, 3

My swift code looks like this...

var dataPoints: Dictionary<String,Int> = ["A":1,"B":2,"C":3]
for (key, value) in dataPoints {
        print(value)
}

However, the values come out in an unexpected order. Is there something clean I can do to keep the values in the order they were created, or can a swift dictionary never be sorted?

Ben
  • 4,707
  • 5
  • 34
  • 55
  • 3
    `Dictionaries`/`NSDictionary` aren't sorted in `Swift` or `Objective-C` but have a look at these http://stackoverflow.com/questions/25377177/sort-dictionary-by-keys & http://stackoverflow.com/questions/30054854/sort-a-dictionary-in-swift – sbarow Nov 30 '15 at 11:00
  • 1
    Here's a custom implementation of ordered dictionary: http://timekl.com/blog/2014/06/02/learning-swift-ordered-dictionaries/ – Cheng-Yu Hsu Nov 30 '15 at 11:03

5 Answers5

10

As has already been answered, the point of a dictionary is that it is not sorted. There are three types of collections in Swift (and Objective-C)

An array is an ordered list of items. Use this when the order of the items is important.

A dictionary is an unordered collection of keys and values. Use this when you want to efficiently retrieve a value based on a key.

A set is a bag of unordered, unique items. Use this when you want to have a collection of unique items in no particular order.

For your case it seems that the ordering is what is important to you, but you want to store some kind of pair of values. Perhaps what you should be looking at is using an array of tuples:

something like this, maybe

let dataPoints = [("A", 1), ("B", 2), ("C", 3)]

for (letter, number) in dataPoints {
    print("\(letter), \(number)")
}

which outputs

A, 1
B, 2
C, 3

Alternatively

If you don't know about the order of the items before their creation, but there is a natural ordering based on the keys, you could do something like this:

let dict = [
    "A" : 1,
    "B" : 2,
    "C" : 3
]

for key in dict.keys.sort() {
    guard let value = dict[key] else { break }
    print("\(key), \(value)")
}

In this case you get an array of keys with their default sort order (you can use a different sort function if you have other requirements) and output the key values based on that sorted order.

Abizern
  • 146,289
  • 39
  • 203
  • 257
4

From Apple docs:

Unlike items in an array, items in a dictionary do not have a specified order.

So, no, Swift dictionary are never "sorted". However, a quick Google search for "Swift ordered dictionary "will yield several implementation which may serve your needs:

For example:

https://github.com/lukaskubanek/OrderedDictionary

https://github.com/Marxon13/M13DataStructures/blob/master/Classes/OrderedDictionary.swift

Cocoapods could also help:

https://cocoapods.org/?q=ordered%20dict

Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
  • Thanks Guillaume, that's very useful. I have a couple of further questions, if you don't mind: 1 - What if the dictionary is created from a JSON string - can you never know the original order? 2 - Is there a good key/value data type to use, so that I could create an ordered Array of them instead? – Ben Nov 30 '15 at 11:10
  • 3
    1- No that changes nothing, a dictionary never retains order. Note that this is also the case for JSON "dictionaries" (https://stackoverflow.com/questions/7198412/keeping-dictionary-keys-of-json-object-in-order-in-objective-c) they don't have order either. 2- Depending on your needs, you could store your data into an array of structs or classes, which would retain order and have strong typing. – Guillaume Algis Nov 30 '15 at 11:48
2

You can now use the OrderedDictionary from the official Apple Collections library: https://github.com/apple/swift-collections

Teo Sartori
  • 1,082
  • 14
  • 24
  • Direct link to OrderedDictionary examples: https://github.com/apple/swift-collections/blob/main/Documentation/OrderedDictionary.md – Sentry.co Jul 14 '21 at 11:31
1

Just in case this is useful, based on the responses here and because I was receiving data from a JSON source, I decided the best approach was for me to convert my dictionary into a sorted array of tuples. I created the following function for this:

func dictionary_to_sorted_array(dict:Dictionary<String,Int>) ->Array<(key:String,value:Int)> {
    var tuples: Array<(key:String,value:Int)> = Array()
    let sortedKeys = (dict as NSDictionary).keysSortedByValueUsingSelector("compare:")
    for key in sortedKeys {
        tuples.append((key:key as! String,value:dict[key as! String]!))
    }
    return tuples
}

So then, I could use the following code:

var dataPoints: Dictionary<String,Int> = ["A":1,"B":2,"C":3]
dataPointsArray(dictionary_to_sorted_array(dataPoints))
for dataPoint in dataPointsArray {
    print(dataPoint.value)
}

I'm very much a beginner with swift, so if anybody has a quicker way of doing this, that would be great!

Ben
  • 4,707
  • 5
  • 34
  • 55
1

From Swift 4, you can use sorted(by:) to return a sorted array of the dictionary's elements (tuples). So the code from your question becomes:

var dataPoints = ["A":1,"B":2,"C":3]

var sortedDataPoints = dataPoints.sorted(by: { $0.value < $1.value })
// [(key: "A", value: 1), (key: "B", value: 2), (key: "C", value: 3)]

for (_, value) in sortedDataPoints {
    print(value)
}
Kal
  • 2,098
  • 24
  • 23