字典数组中最常见的字典

时间:2015-06-19 19:13:39

标签: arrays swift dictionary

我需要在快速字典数组中找到最常用的字典。我尝试使用以下内容:

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'。如何编辑上述函数以获取字典数组,或完全使用其他方法?

3 个答案:

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