Chris Koknat Chris Koknat - 5 months ago 105
Swift Question

Swift: Sort dictionary keys by value, then by key

I'd like to iterate over the keys of a dictionary, sorted first by value (descending), and then by key (ascending)

let dict = ["foo" : 1, "bar" : 1, "baz" : 2, "qux" : 2]


The order of the iteration should be:

["baz", "qux", "bar", "foo"]


I'd like to print:

baz 2
qux 2
bar 1
foo 1

Answer

Xcode 8 beta • Swift 3

extension Dictionary where Value: Comparable {
    var valueKeySorted: [(key: Key, value: Value)] {
        return sorted(isOrderedBefore: { $0.1 != $1.1 ? $0.1 > $1.1 : String($0.0) < String($1.0) })
    }
}


let dict = ["foo" : 1, "bar" : 1, "baz" : 2, "qux" : 2]

let keyValueArray = dict.valueKeySorted

print(keyValueArray)   // "[("baz", 2), ("qux", 2), ("bar", 1), ("foo", 1)]"

for (key, value) in keyValueArray {
    print(key, value)
}


Xcode 2.x

let keyValueArray = dict.sort{ $0.0 < $1.0 }.sort{ $0.1 > $1.1}
print(keyValueArray)  // [(.0 "baz", .1 2), (.0 "qux", .1 2), (.0 "bar", .1 1), (.0 "foo", .1 1)]

You can also create an extension where dictionaries values are comparable:

extension Dictionary where Value: Comparable {
    var valueKeySorted: [(Key, Value)] {
        return sort{ $0.1 > $1.1 }.sort{ String($0.0) < String($1.0) }
    }
    // or sorting as suggested by Just Another Coder without using map
    var valueKeySorted2: [(Key, Value)] {
        return sort{ if $0.1 != $1.1 { return $0.1 > $1.1 } else { return String($0.0) < String($1.0) } }
    }


}

usage:

let dict = ["foo" : 1, "bar" : 1, "baz" : 2, "qux" : 2]

let keyValueArray = dict.valueKeySorted

print(keyValueArray)   // "[("baz", 2), ("qux", 2), ("bar", 1), ("foo", 1)]"

If you would like to iterate over keyValueArray of tuples

for (key, value) in keyValueArray {
    print(key, value)
}