Home » Swift » Sort Dictionary by values in Swift

Sort Dictionary by values in Swift

Posted by: admin November 30, 2017 Leave a comment

Questions:

Is there are analog of – (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator in swift?

How to do this without casting to NSDictionary?

I tried this, but it seems to be not a good solution.

var values = Array(dict.values)
values.sort({
    $0 > $1
    })

for number in values {
    for (key, value) in dict {
        if value == number {
            println(key + " : \(value)");
            dict.removeValueForKey(key);
            break
        }
    }
}

Example:

var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
dict.sortedKeysByValues(>) // fanta (12), cola(10), sprite(8)
Answers:

Try:

let dict = ["a":1, "c":3, "b":2]

extension Dictionary {
    func sortedKeys(isOrderedBefore:(Key,Key) -> Bool) -> [Key] {
        return Array(self.keys).sort(isOrderedBefore)
    }

    // Slower because of a lot of lookups, but probably takes less memory (this is equivalent to Pascals answer in an generic extension)
    func sortedKeysByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
        return sortedKeys {
            isOrderedBefore(self[$0]!, self[$1]!)
        }
    }

    // Faster because of no lookups, may take more memory because of duplicating contents
    func keysSortedByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
        return Array(self)
            .sort() {
                let (_, lv) = $0
                let (_, rv) = $1
                return isOrderedBefore(lv, rv)
            }
            .map {
                let (k, _) = $0
                return k
            }
    }
}

dict.keysSortedByValue(<)
dict.keysSortedByValue(>)

Updated:

Updated to the new array syntax and sort semantics from beta 3. Note that I’m using sort and not sorted to minimize array copying. The code could be made more compact, by looking at the earlier version and replacing sort with sorted and fixing the KeyType[] to be [KeyType]

Updated to Swift 2.2:

Changed types from KeyType to Key and ValueType to Value. Used new sort builtin to Array instead of sort(Array) Note performance of all of these could be slightly improved by using sortInPlace instead of sort

Questions:
Answers:

You could use something like this perhaps:

var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]

var myArr = Array(dict.keys)
var sortedKeys = sort(myArr) {
    var obj1 = dict[$0] // get ob associated w/ key 1
    var obj2 = dict[$1] // get ob associated w/ key 2
    return obj1 > obj2
}

myArr // ["fanta", "cola", "sprite"]

Questions:
Answers:

This should give you the sorted keys based on value, and is a little more cleaner:

var sortedKeys = Array(dict.keys).sorted({dict[$0] < dict[$1]})

Questions:
Answers:

Sorting your keys by the dictionary’s value is actually simpler than it appears at first:

let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort({ (firstKey, secondKey) -> Bool in
    return yourDict[firstKey] < yourDict[secondKey]
})

And that’s it! There’s really nothing more to it. I have yet to find a quicker method.

Questions:
Answers:

I think this is the easiest way to sort Swift dictionary by value.

let dict = ["apple":1, "cake":3, "banana":2]

let byValue = {
    (elem1:(key: String, val: Int), elem2:(key: String, val: Int))->Bool in
    if elem1.val < elem2.val {
        return true
    } else {
        return false
    }
}
let sortedDict = dict.sort(byValue)

Questions:
Answers:

Lots of answers, here’s a one-liner. I like it because it makes full use of native Swift iterative functions and doesn’t use variables. This should help the optimiser do its magic.

return dictionary.keys.sort({ $0 < $1 }).flatMap({ dictionary[$0] })

Note the use of flatMap, because subscripting a dictionary returns an optional value. In practice this should never return nil since we get the key from the dictionary itself. flatMap is there only to ensure that the result is not an array of optionals. If your array’s associated value should BE an optional you can use map instead.

Questions:
Answers:

Just cast it to NSDictionary and then call the method. Anywhere you use @selector in ObjC you can just use a String in Swift. So it would look like this:

var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let sortedKeys = (dict as NSDictionary).keysSortedByValueUsingSelector("compare:")

or

let sortedKeys2 = (dict as NSDictionary).keysSortedByValueUsingComparator 
                  { 
                       ($0 as NSNumber).compare($1 as NSNumber) 
                  }

Questions:
Answers:

As of Swift 3, to sort your keys based on values, the below looks promising:

var keys = Array(dict.keys)        
keys.sortInPlace { (o1, o2) -> Bool in
    return dict[o1]! as! Int > dict[o2]! as! Int
}

Questions:
Answers:

OneLiner :

let dict = ["b":2,"a":1,"c":3]
(Array(dict).sorted{$0.1 < $1.1}).forEach{(k,v) in print("\(k):\(v)")}
//Output: a:1, b:2, c:3

Swap out the .forEach with .map -> Functional programming

Syntactical sugar :

extension Dictionary where Value:Comparable {
    var sortedByValue:[(Key,Value)] {return Array(self).sorted{$0.1 < $1.1}}
}
extension Dictionary where Key:Comparable {
    var sortedByKey:[(Key,Value)] {return Array(self).sorted{$0.0 < $1.0}}
}
["b":2,"a":1,"c":3].sortedByKey//a:1, b:2, c:3
["b":2,"a":1,"c":3].sortedByValue//a:1, b:2, c:3

Questions:
Answers:

The following way in Swift 3 sorted my dictionary by value in the ascending order:

for (k,v) in (Array(dict).sorted {$0.1 < $1.1}) {
    print("\(k):\(v)")
}

Questions:
Answers:

SWIFT 3:

Using a few resources I put this beautifully short code together.

dictionary.keys.sorted{dictionary[$0]! < dictionary[$1]!}

This returns an array of the dictionary keys sorted by their values. It works perfectly & doesn’t throw errors when the dictionary is empty. Try this code in a playground:

//: Playground - noun: a place where people can play

import UIKit

let dictionary = ["four": 4, "one": 1, "seven": 7, "two": 2, "three": 3]

let sortedDictionary = dictionary.keys.sorted{dictionary[$0]! < dictionary[$1]!}

print(sortedDictionary)
// ["one", "two", "three", "four", "seven"]


let emptyDictionary = [String: Int]()

let emptyDictionarySorted = emptyDictionary.keys.sorted{emptyDictionary[$0]! < emptyDictionary[$1]!}

print(emptyDictionarySorted)
// []

If you’d like some help on why the heck the code uses $0, $1 and doesn’t even have parentheses after the “sorted” method, check out this post – https://stackoverflow.com/a/34785745/7107094

Questions:
Answers:

This is how I did it – sorting in this case by a key called position. Try this in a playground:

var result: [[String: AnyObject]] = []
result.append(["name" : "Ted", "position": 1])
result.append(["name" : "Bill", "position": 0])
result


result = sorted(result, positionSort)

func positionSort(dict1: [String: AnyObject], dict2: [String: AnyObject]) -> Bool {
    let position1 = dict1["position"] as? Int ?? 0
    let position2 = dict2["position"] as? Int ?? 0
    return position1 < position2
}

Questions:
Answers:

The following might be useful if you want the output to be an array of key value pairs in the form of a tuple, sorted by value.

var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let sortedArrByValue = dict.sorted{$0.1 > $1.1}
print(sortedArrByValue) // output [(key: "fanta", value: 12), (key: "cola", value: 10), (key: "sprite", value: 8)]