我需要在快速字典数组中找到最常用的字典。我尝试使用以下内容:
func frequencies
<S: SequenceType where S.Generator.Element: Hashable>
(source: S) -> [(S.Generator.Element,Int)] {
var frequency: [S.Generator.Element:Int] = [:]
for x in source {
frequency[x] = (frequency[x] ?? 0) + 1
}
return sorted(frequency) { $0.1 > $1.1 }
}
但我无法使用[[String:String]]()
类型的参数列表调用'frequency'。如何编辑上述函数以获取字典数组,或完全使用其他方法?
答案 0 :(得分:3)
正如评论所提到的,问题是[String:String]
类型不是Hashable
。
当您的类型不可清除时,(效率)效率较低的解决方案是回退到Comparable
(可以排序并生成运行总计)或Equatable
,或者最坏的情况下,需要调用者提供一个isEquivalent
关闭。然后你去搜索你的运行频率搜索一个等价的项目(如果你没有找到一个,插入频率为1)。
这是一个在Swift 2.0中实现的实现:
extension SequenceType {
func frequencies(@noescape isEquivalent: (Generator.Element,Generator.Element) -> Bool) -> [(Generator.Element,Int)] {
var frequency: [(Generator.Element,Int)] = []
for x in self {
// find the index of the equivalent entry
if let idx = frequency.indexOf({ isEquivalent($0.0, x)}) {
// and bump the frequency
frequency[idx].1 += 1
}
else {
// add a new entry
frequency.append(x,1)
}
}
return frequency.sort { $0.1 > $1.1 }
}
}
由于==
的实现比较了两个词典,只要这些词典包含相同的值,就可以这样调用它:
let dicts = [
["name": "David", "number": "1"],
["name": "John", "number": "2"],
["name": "David", "number": "1"],
]
// you can use `==` in two dictionaries that contain an equatable value,
// such as String here:
dicts[0] == dicts[1] // false
dicts[0] == dicts[2] // true
// so you can call frequencies like so:
dicts.frequencies(==)
返回:
[(["number": "1", "name": "David"], 2),
(["number": "2", "name": "John"], 1)]
编辑:这是一个Swift 1.2版本,遗憾的是,由于缺少版本为find
的1.2(在2.0中重命名为indexOf
)而采用谓词。这应该有效,但我在这台机器上没有1.2的工作副本,所以你可能需要修复任何语法错误:
extension Array {
// add missing indexOf to Array as 1.2 doesn't have an equivalent
func indexOf(@noescape predicate: T->Bool) -> Int? {
for idx in indices(self) {
if predicate(self[idx]) { return idx }
}
return nil
}
}
func frequencies<S: SequenceType>
(source: S, @noescape isEquivalent: (S.Generator.Element,S.Generator.Element) -> Bool) -> [(S.Generator.Element,Int)] {
var frequency: [(S.Generator.Element,Int)] = []
for x in source {
// find the index of the equivalent entry
if let idx = frequency.indexOf({ isEquivalent($0.0, x)}) {
// and bump the frequency
frequency[idx].1 += 1
}
else {
// add a new entry
frequency.append(x,1)
}
}
return sorted(frequency) { $0.1 > $1.1 }
}
frequencies(dicts, ==)
答案 1 :(得分:0)
您可以使用提供此类功能的NSCountedSet
:
let arr = [
[
"foo": "bar"
],
[
"foo": "bar"
],
[
"foo": "baz"
],
]
let countedSet = NSCountedSet(array: arr)
for dict in countedSet {
println(countedSet.countForObject(dict))
println(dict)
}
打印
2
[&#34; foo&#34;:&#34; bar&#34;]
1
[&#34; foo&#34;:&#34; baz&#34;]
如果您在初始化程序无法正常使用时遇到问题,请使用:
let countedSet = NSCountedSet()
countedSet.addObjectsFromArray(arr)
而不是
let countedSet = NSCountedSet(array: arr)
我还没有使用Swift 2进行测试,但使用情况应该或多或少相同。
答案 2 :(得分:-1)
(我不知道这是否适用于Swift 1.2)
您还可以使用全局功能:
func frequencies<K, V where K : Hashable, V: Equatable>(dicts: [[K : V]]) -> [(dict: [K : V], count: Int)] {
var counts = [(dict: [K : V], count: Int)]()
for dict in dicts {
if let index = counts.indexOf({ $0.dict == dict }) {
counts[index].1++
} else {
counts.append(dict: dict, count: 1)
}
}
return counts.sort { $0.1 > $1.1 }
}
像这样使用
let dicts = [
["name": "David", "number": "1"],
["name": "John" , "number": "2"],
["name": "David", "number": "1"],
]
print(frequencies(dicts))
输出
[(["number": "1", "name": "David"], 2),
(["number": "2", "name": "John" ], 1)]