Sort Dictionary by values in Swift

Swift

Swift Problem Overview


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)

Swift Solutions


Solution 1 - Swift

Just one line code to sort dictionary by Values in Swift 4, 4.2 and Swift 5:

let sortedByValueDictionary = myDictionary.sorted { $0.1 < $1.1 }

Solution 2 - Swift

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

Solution 3 - Swift

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"]

Solution 4 - Swift

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

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

Solution 5 - Swift

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)

Solution 6 - Swift

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

Solution 7 - Swift

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.

Solution 8 - Swift

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, other than the same approach in form of a simple one-liner:

let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort { yourDict[$0] < yourDict[$1] }

Solution 9 - Swift

Sorting a dictionary by key or value

Using Swift 5.2 internal handling of "sorted":

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

// sorting by value
let sortedDictByValue = unsortedDict.sorted{ $0.value > $1.value } // from lowest to highest using ">"
print("sorted dict: \(sortedDictByValue)")
// result: "sorted dict: [(key: "fanta", value: 12), (key: "cola", value: 10), (key: "sprite", value: 8)]\n"

// highest value
print(sortedDictByValue.first!.key)  // result: fanta
print(sortedDictByValue.first!.value)  // result: 12
// lowest value
print(sortedDictByValue.last!.key)  // result: sprite
print(sortedDictByValue.last!.value)  // result: 8
// by index
print(sortedDictByValue[1].key)  // result: cola
print(sortedDictByValue[1].value)  // result: 10

// sorting by key
let sortedDictByKey = unsortedDict.sorted{ $0.key < $1.key } // in alphabetical order use "<"
// alternative:
// let sortedDictByKey = unsortedDict.sorted{ $0 < $1 }  // without ".key"
print("sorted dict: \(sortedDictByKey)")
// result: "sorted dict: [(key: "cola", value: 10), (key: "fanta", value: 12), (key: "sprite", value: 8)]\n"

// highest value
print(sortedDictByKey.first!.key)  // result: cola
print(sortedDictByKey.first!.value)  // result: 10
// lowest value
print(sortedDictByKey.last!.key)  // result: sprite
print(sortedDictByKey.last!.value)  // result: 8
// by index
print(sortedDictByKey[1].key)  // result: fanta
print(sortedDictByKey[1].value)  // result: 12

Solution 10 - Swift

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)]

Solution 11 - Swift

Since Swift 3.0 Dictionary has sorted(by:) function which returns an array of tuples ([(Key, Value)]).

let sorted = values.sorted(by: { (keyVal1, keyVal2) -> Bool in
    keyVal1.value > keyVal2.value
})

Solution 12 - Swift

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) 
                  }

Solution 13 - Swift

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
}

Solution 14 - Swift

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

let arr = dic.sort{ (d1,d2)-> Bool in
if d1.value > d2.value {
    retrn true
}

}.map { (key,value) -> Int in
return value
}

Take look a clean implementation way. print("arr is :(arr)")

Solution 15 - Swift

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)")
}

Solution 16 - Swift

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

Solution 17 - Swift

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
}

Solution 18 - Swift

Sorting the dictionary with a dictionary as the value (Nested dictionary)

   var students: [String: [String: Any?]] = ["16CSB40" : ["Name": "Sunitha", "StudentId": "16CSB40", "Total": 90], "16CSB41" : ["Name": "Vijay", "StudentId": "16CSB40", "Total": 80], "16CSB42" : ["Name": "Tony", "StudentId": "16CSB42", "Total": 95]] // Sort this dictionary with total value
        
    let sorted = students.sorted { (($0.1["Total"] as? Int) ?? 0) < (($1.1["Total"] as? Int) ?? 0) }
        
    print(sorted) //Sorted result

Solution 19 - Swift

Use this, and then just loop through the dictionary again using the output keys.

extension Dictionary where Value: Comparable {
  func sortedKeysByValue() -> [Key] {
    keys.sorted { return self[$0]! < self[$1]! }
  }
}

...or this if you hate force unwrapping :)

extension Dictionary where Value: Comparable {
  func sortedKeysByValue() -> [Key] {
    keys.sorted { (key1, key2) -> Bool in
      guard let val1 = self[key1] else { return true }
      guard let val2 = self[key2] else { return true }
      return val1 < val2
    }
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNikeAliveView Question on Stackoverflow
Solution 1 - SwiftS.S.DView Answer on Stackoverflow
Solution 2 - SwiftDavid BerryView Answer on Stackoverflow
Solution 3 - SwiftLoganView Answer on Stackoverflow
Solution 4 - Swiftpulse4lifeView Answer on Stackoverflow
Solution 5 - Swiftjeongmin.chaView Answer on Stackoverflow
Solution 6 - SwiftSentry.coView Answer on Stackoverflow
Solution 7 - SwiftAshView Answer on Stackoverflow
Solution 8 - SwiftMartinView Answer on Stackoverflow
Solution 9 - Swiftknoepffler.netView Answer on Stackoverflow
Solution 10 - SwiftcypherView Answer on Stackoverflow
Solution 11 - SwiftkelinView Answer on Stackoverflow
Solution 12 - SwiftahrussView Answer on Stackoverflow
Solution 13 - SwiftjudepereiraView Answer on Stackoverflow
Solution 14 - Swiftknight2016View Answer on Stackoverflow
Solution 15 - SwiftDarius MiliauskasView Answer on Stackoverflow
Solution 16 - SwiftTrev14View Answer on Stackoverflow
Solution 17 - SwiftCodezyView Answer on Stackoverflow
Solution 18 - SwiftBalaji MalliswamyView Answer on Stackoverflow
Solution 19 - SwiftHlungView Answer on Stackoverflow