在Swift中按值对字典进行排序

56

在Swift中是否有类似于-(NSArray *)keysSortedByValueUsingSelector:(SEL)comparator的方法?

如何在不将类型强制转换为NSDictionary的情况下实现这一点?

我尝试了这个,但似乎不是一个好的解决方案。

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

例子:

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

https://dev59.com/qlwZ5IYBdhLWcg3wYfZu#31528848 - Leo Dabus
19个回答

97

在 Swift 4、4.2 和 Swift 5 中,只需一行代码即可按值对字典进行排序:

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

2
在Swift 4.1中,使用Dictionary<String, Int>对我很有效。 - eharo2
2
N.B. 返回一个元组数组(key,value),如果您想要对象,则需要像这样读取内容:array[x].1 - Ash
1
我对Swift还比较新,认为通过简要解释语法{ $0.1 < $1.1 }可以使这个答案更加强大。 - John Harrington
谢谢,@Ash提供的提示!在我的情况下,我需要通过执行以下操作获取每个元组的键:array[x].key - jakob.j
另外,就我的情况而言,我需要按字符串中的数字值进行排序,因此最终使用了以下代码:let sortedByValueDictionary = myDictionary.sorted {$0.value.localizedStandardCompare($1.value) == .orderedAscending}(灵感来自于 https://dev59.com/hVcP5IYBdhLWcg3wxMs5#43870210) - jakob.j
3
@JohnHarrington:作为一种尝试解释的方式:$0$1是定义比较函数的“两个伪条目”(然后由sorted函数应用于字典的所有条目)。由于我们想按值进行比较,因此必须使用第二个元素(0是键,1是值),因此是.1。(因此,您也可以编写{ $0.value < $1.value }。) - jakob.j

28

尝试:

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

已更新:

使用beta 3的新数组语法和排序语义进行了更新。请注意,我使用的是sort而不是sorted以最小化数组复制。可以通过查看早期版本并将sort替换为sorted并修复KeyType[][KeyType]来使代码更加简洁。

已更新到Swift 2.2:

将类型从KeyType更改为Key,将ValueType更改为Value。使用了新的sort内置函数来操作Array,而不是sort(Array)。请注意,所有这些性能都可以通过使用sortInPlace而不是sort稍微提高。


1
@NikeAlive 已经根据您的需求进行了编辑,感谢您提供的问题 :) - David Berry
他们已经改变了 sort 的语义,现在它接收一个 in-out 数组,并在原地排序。旧的功能现在作为 sorted 提供。明天我会在电脑上更新答案,以验证所有内容。 - David Berry
2
以上代码在Xcode 6 Beta5中给我带来了错误:“使用未声明的类型'KeyType'”,如果我将所有的KeyType/ValueType更改为Int,编译器会说:“'Key'不是'Int'的子类型”和“'Key'与'Int'不相同”在第一个“func SortedKeys…”中。有关Xcode6 Beta5的任何想法或修复方法吗? - iOS-Coder
从Swift 1.2和Xcode 6.3.1开始,为了在此扩展中进行编译,需要更改的是将KeyType全部更改为Key,并将ValueType更改为Value - Phil Andrews
已更新答案以符合Swift 2.2语法和函数。 - David Berry
显示剩余10条评论

24

也许你可以使用类似这样的东西:

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

1
以上代码在Xcode6 Beta5中会出现以下错误:"Passing value of type 'Array<String>' to an inout parameter requires explicit '&'". 但是,如果我将其修复为"var sortedKeys = sort(&myArr)",则会收到警告:"Variable 'sortedKeys' inferred to have type '()', which may be unexpected"。将其修复为"var sortedKeys: () = sort(&myArr)"可以得到所需的结果。有没有办法按键排序(如果可能的话)?请参见我对@David答案中的评论。 - iOS-Coder
6
在 Swift 4 中,sort 不再存在。原文意思为:Swift 4 中不再支持 sort 方法。 - Van Du Tran
2
使用 myArr.sorted() 来进行 Swift 4 的排序。 - Kanika Sharma

21
这应该会给你基于值排序的键,并且更加简洁:
var sortedKeys = Array(dict.keys).sorted(by: { dict[$0]! < dict[$1]! })

2
请考虑在您的答案中添加解释,仅有代码的答案不被认为是好的。 - Leandros

12

一句话概括:

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

.map 替换 .forEach -> 函数式编程

语法糖:

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

1
这些表情符号很烦人。 - vikingosegundo
4
@vikingosegundo 好的 ¯_(ツ)_/¯ - Sentry.co
@andy 感谢您的编辑,但是一些重要的上下文被删除了。我不得不重新编辑。Stackoverflow 对表情符号的规定是什么? - Sentry.co
1
你的回答的第二部分中未定义dict。我认为应该更改为self。 - Titouan de Bailleul
@Titouan de Bailleul 谢谢。已更新。 - Sentry.co
让字典 = ["b":"2","a":"1","c":"3"].sortedByKey 打印("字典 ::: (dict)") 字典 ::: [("a", "1"), ("b", "2"), ("c", "3")], 实际结果应该是字典 ::: ["a":"1", "b": "2", "c": "3"] - MuraliDharan V

11

我认为这是按值对Swift字典进行排序的最简单方法。

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)

1
非常感谢 :) 可以在 Swift5 中使用(sort 现在是 sorted(by: byValue)) - Skyborg
但是返回的值是键值对,而不是字典 :( - MuraliDharan V
@MuraliDharanV 我可能不太准确,因为我最近没有开发iOS,但在大多数编程语言中,键值对被称为字典。 - jeongmin.cha

8

有很多答案,这里是一个简短的代码。我喜欢它,因为它充分利用了本地Swift迭代函数,并且不使用变量。这应该有助于优化器发挥其魔力。

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

请注意使用flatMap,因为从字典中获取下标会返回一个可选值。在实践中,这应该永远不会返回nil,因为我们从字典本身获取键。 flatMap只是为了确保结果不是可选数组。如果您的数组的关联值应该是可选的,可以使用map代替。

8

按照字典值对键进行排序实际上比一开始看起来的要简单:

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

就是这样!其实没有更简单的方法了。除了这个简单的一行代码,我还没有找到更快的方法:

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

6

按键或值对字典进行排序

使用Swift 5.2内部处理的 "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

当然可以!我不明白为什么这不是被接受的答案。 - Santi Pérez

5
如果您想要将输出以元组的形式呈现为键值对的数组,并按值进行排序,则以下内容可能会有所帮助。
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)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接