在Swift中按键分组字典

时间:2016-04-13 14:06:19

标签: swift dictionary

我正在尝试实现groupBy功能,其中嵌套列表的所有数字都被分组。到目前为止我的代码:

struct MyClass {
    var numbers: [Int]
    ...
}

var dict: [String : MyClass] = ...
let numbers = dict
   .filter{ $0.0.containsString(searchString) }
   .flatMap{ $0.1.numbers }

这会产生Array Int秒。但是我想要一个字典[Int:Int],每个唯一的数字和它的出现次数。例如:

[1,2,3,4,1,2,2,1]

应该是:

[1 : 2, 2 : 3, 3 : 1, 4 : 1]

我知道有groupBy运算符,但Swift似乎没有运算符。我试过reduce

func reducer(accumulator: [Int: Int], num: Int) -> [Int : Int] {
    var acc = accumulator
    acc[num]! += 1
    return acc
}

filtered.reduce([:], combine: reducer)

但是当我想要运行它时崩溃了。不知道为什么,我得到一个EXC_BAD_INSTRUCTION。

我很感激任何帮助。

5 个答案:

答案 0 :(得分:3)

我希望崩溃发生在这条线上:

acc[num]! += 1

第一次调用此数字时,该字词在字典中不存在,因此acc[num]nil。强行打开它会导致崩溃。

不确定这是否是最佳解决方案,但您可以简单地检查这种情况:

if (acc[num]) {
    acc[num]! += 1
} else {
    acc[num] = 1
}

评论中来自@vacawama的清洁代码:

acc[num] = (acc[num] ?? 0) + 1

答案 1 :(得分:3)

let numbers = [1,2,3,4,1,2,2,1]
var results = [Int: Int]()

Set(numbers).forEach { number in results[number] = numbers.filter { $0 == number }.count }

print(results) // [2: 3, 3: 1, 1: 3, 4: 1]

其实我不太确定这是不是你想要的。我只看了你的例子。

使用NSCountedSet

var objects = [1,2,3,4,1,2,2,1]
let uniques = NSCountedSet(array: objects)
uniques.forEach { results[$0 as! Int] = uniques.countForObject($0) }

print(results) // [2: 3, 3: 1, 1: 3, 4: 1]

答案 2 :(得分:2)

这是Array的扩展,可以满足您的要求:

extension Array where Element: Hashable {
  var grouped: [Element:Int] {
    var dict = [Element:Int]()
    self.forEach { dict[$0] = (dict[$0] ?? 0) + 1 }
    return dict
  }
}

关键是闭包:{ dict[$0] = (dict[$0] ?? 0) + 1 }

它接受数组中的当前值,测试它是否是字典中的键,如果存在则返回该键的值,如果不存在,则返回0,然后添加一个并设置关键:值为当前值和当前事件的对。

使用示例:

[1,2,3,4,1,2,2,1].grouped // => [2: 3, 3: 1, 1: 3, 4: 1]

答案 3 :(得分:0)

你需要这样的东西:

if let _ = acc.indexForKey(num) {
    acc[num]! += 1
}
else {
    acc[num] = 1
}

答案 4 :(得分:0)

它有点不清楚你要求的是什么,但这里有一个函数,它将采用一系列的int并返回一个以数字为键的字典,并将计数作为值:

func getDictionaryOfCounts(accumulator: [Int]) -> [Int : Int] {
    var countingDictionary: [Int : Int] = [:]
    accumulator.forEach { (value) in
        if countingDictionary[value] != nil {
            countingDictionary[value]! += 1
        }
        else{
            countingDictionary[value] = 1
        }
    }
    return countingDictionary
}