如何计算Swift数组中元素的出现?

我已经看到了一些这样的例子,但所有这些似乎都依赖于知道哪个元素要计算出现的次数。 我的数组是dynamic生成的,所以我无法知道哪个元素我想要计数(我想要统计所有这些事件的发生)。 任何人都可以build议吗?

提前致谢

编辑:

也许我应该更清楚,数组将包含多个不同的string(例如["FOO", "FOO", "BAR", "FOOBAR"]

我怎么能计算foo,bar和foobar的出现而不知道它们是什么?

Swift 3和Swift 2:

您可以使用[String: Int]types的字典为[String: Int]中的每个项目build立计数:

 let arr = ["FOO", "FOO", "BAR", "FOOBAR"] var counts: [String: Int] = [:] for item in arr { counts[item] = (counts[item] ?? 0) + 1 } print(counts) // "[BAR: 1, FOOBAR: 1, FOO: 2]" for (key, value) in counts { print("\(key) occurs \(value) time(s)") } 

输出:

 BAR occurs 1 time(s) FOOBAR occurs 1 time(s) FOO occurs 2 time(s) 

Swift 4:

Swift 4 引入了(SE-0165)在字典查找中包含缺省值的能力,并且结果值可以用诸如+=-=操作进行变异,所以:

 counts[item] = (counts[item] ?? 0) + 1 

变为:

 counts[item, default: 0] += 1 

这使得使用forEach可以轻松地在一条简洁的线上进行计数操作:

 let arr = ["FOO", "FOO", "BAR", "FOOBAR"] var counts: [String: Int] = [:] arr.forEach { counts[$0, default: 0] += 1 } print(counts) // "["FOOBAR": 1, "FOO": 2, "BAR": 1]" 

Swift 4: reduce(into:_:)

Swift 4引入了reduce的新版本,它使用inoutvariables来累计结果。 利用这一点,计数的创build真正成为一条线:

 let arr = ["FOO", "FOO", "BAR", "FOOBAR"] let counts = arr.reduce(into: [:]) { counts, word in counts[word, default: 0] += 1 } print(counts) // ["BAR": 1, "FOOBAR": 1, "FOO": 2] 

或者使用默认参数:

 let counts = arr.reduce(into: [:]) { $0[$1, default: 0] += 1 } 
 array.filter{$0 == element}.count 

我更新了oisdk对Swift2的回答 。

16/04/14我更新了这个代码到Swift2.2

16/10/11更新为Swift3


哈希的:

 extension Sequence where Self.Iterator.Element: Hashable { private typealias Element = Self.Iterator.Element func freq() -> [Element: Int] { return reduce([:]) { (accu: [Element: Int], element) in var accu = accu accu[element] = accu[element]?.advanced(by: 1) ?? 1 return accu } } } 

Equatable:

 extension Sequence where Self.Iterator.Element: Equatable { private typealias Element = Self.Iterator.Element func freqTuple() -> [(element: Element, count: Int)] { let empty: [(Element, Int)] = [] return reduce(empty) { (accu: [(Element, Int)], element) in var accu = accu for (index, value) in accu.enumerated() { if value.0 == element { accu[index].1 += 1 return accu } } return accu + [(element, 1)] } } } 

用法

 let arr = ["a", "a", "a", "a", "b", "b", "c"] print(arr.freq()) // ["b": 2, "a": 4, "c": 1] print(arr.freqTuple()) // [("a", 4), ("b", 2), ("c", 1)] 

 for (k, v) in arr.freq() { print("\(k) -> \(v) time(s)") } // b -> 2 time(s) // a -> 4 time(s) // c -> 1 time(s) for (element, count) in arr.freqTuple() { print("\(element) -> \(count) time(s)") } // a -> 4 time(s) // b -> 2 time(s) // c -> 1 time(s) 

使用Swift 4,根据您的需要,您可以select以下6个Playground代码中的一个来计算数组中可哈希项的出现次数。


#1。 使用Array reduce(into:_:)Dictionary subscript(_:default:) (需要斯威夫特4)

 let array = [4, 23, 97, 97, 97, 23] let reducedArray = array.reduce(into: [:]) { counts, number in counts[number, default: 0] += 1 } print(reducedArray) // [4: 1, 23: 2, 97: 3] 

#2。 使用repeatElement(_:count:)函数, zip(_:_:)函数, Dictionary init(_:uniquingKeysWith:)初始化程序并返回一个Dictionary (需要Swift 4)

 let array = [4, 23, 97, 97, 97, 23] let repeated = repeatElement(1, count: array.count) //let repeated = Array(repeating: 1, count: array.count) // also works let zipSequence = zip(array, repeated) let dictionary = Dictionary(zipSequence, uniquingKeysWith: { (current, new) in return current + new }) //let dictionary = Dictionary(zipSequence, uniquingKeysWith: +) // also works print(dictionary) // prints [4: 1, 23: 2, 97: 3] 

#3。 使用Dictionary init(grouping:by:)初始化器并返回一个元组数组(需要Swift 4)

 let array = [4, 23, 97, 97, 97, 23] let dictionary = Dictionary(grouping: array, by: { $0 }) let newArray = dictionary.map { (key: Int, value: [Int]) in return (key, value.count) } print(newArray) // prints: [(4, 1), (23, 2), (97, 3)] 

#4。 使用for循环并返回一个Dictionary

 extension Array where Element: Hashable { func countForElements() -> [Element: Int] { var counts = [Element: Int]() for element in self { counts[element] = (counts[element] ?? 0) + 1 } return counts } } let array = [4, 23, 97, 97, 97, 23] print(array.countForElements()) // prints [4: 1, 23: 2, 97: 3] 

#5。 使用NSCountedSetmap方法并返回一个元组数组(需要Foundation)

 import Foundation extension Array where Element: Hashable { func countForElements() -> [(Element, Int)] { let countedSet = NSCountedSet(array: self) let res = countedSet.objectEnumerator().map { (object: Any) -> (Element, Int) in return (object as! Element, countedSet.count(for: object)) } return res } } let array = [4, 23, 97, 97, 97, 23] print(array.countForElements()) // prints [(97, 3), (4, 1), (23, 2)] 

#6。 使用NSCountedSetAnyIterator并返回一个元组Array (需要Foundation)

 import Foundation extension Array where Element: Hashable { func counForElements() -> Array<(Element, Int)> { let countedSet = NSCountedSet(array: self) var countedSetIterator = countedSet.objectEnumerator().makeIterator() let anyIterator = AnyIterator<(Element, Int)> { guard let element = countedSetIterator.next() as? Element else { return nil } return (element, countedSet.count(for: element)) } return Array<(Element, Int)>(anyIterator) } } let array = [4, 23, 97, 97, 97, 23] print(array.counForElements()) // [(97, 3), (4, 1), (23, 2)] 

积分:

  • 快速成语
  • 通用集合,使用词典

使用NSCountedSet。 在Objective-C中:

 NSCountedSet* countedSet = [[NSCountedSet alloc] initWithArray:array]; for (NSString* string in countedSet) NSLog (@"String %@ occurs %zd times", string, [countedSet countForObject:string]); 

我假设你可以自己把它翻译成Swift。

怎么样:

 func freq<S: SequenceType where S.Generator.Element: Hashable>(seq: S) -> [S.Generator.Element:Int] { return reduce(seq, [:]) { (var accu: [S.Generator.Element:Int], element) in accu[element] = accu[element]?.successor() ?? 1 return accu } } freq(["FOO", "FOO", "BAR", "FOOBAR"]) // ["BAR": 1, "FOOBAR": 1, "FOO": 2] 

它是通用的,所以它可以和任何你的元素一起工作,只要它是可以被使用的:

 freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3] freq([true, true, true, false, true]) // [false: 1, true: 4] 

而且,如果你不能让你的元素可拆分,你可以用元组来实现:

 func freq<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [(S.Generator.Element, Int)] { let empty: [(S.Generator.Element, Int)] = [] return reduce(seq, empty) { (var accu: [(S.Generator.Element,Int)], element) in for (index, value) in enumerate(accu) { if value.0 == element { accu[index].1++ return accu } } return accu + [(element, 1)] } } freq(["a", "a", "a", "b", "b"]) // [("a", 3), ("b", 2)] 

另一种方法是使用filter方法。 我觉得最优雅

 var numberOfOccurenses = countedItems.filter( { if $0 == "FOO" || $0 == "BAR" || $0 == "FOOBAR" { return true }else{ return false } }).count 

我喜欢避免内部循环,尽可能使用.map。 所以,如果我们有一个string数组,我们可以做以下计算事件

 var occurances = ["tuples", "are", "awesome", "tuples", "are", "cool", "tuples", "tuples", "tuples", "shades"] var dict:[String:Int] = [:] occurances.map{ if let val: Int = dict[$0] { dict[$0] = val+1 } else { dict[$0] = 1 } } 

版画

 ["tuples": 5, "awesome": 1, "are": 2, "cool": 1, "shades": 1] 

斯威夫特4

 let array = ["FOO", "FOO", "BAR", "FOOBAR"] // Merging keys with closure for conflicts let mergedKeysAndValues = Dictionary(zip(array, repeatElement(1, count: array)), uniquingKeysWith: +) // mergedKeysAndValues is ["FOO": 2, "BAR": 1, "FOOBAR": 1] 

计数sorting的第一步。

 var inputList = [9,8,5,6,4,2,2,1,1] var countList : [Int] = [] var max = inputList.maxElement()! // Iniate an array with specific Size and with intial value. // We made the Size to max+1 to integrate the Zero. We intiated the array with Zeros because it's Counting. var countArray = [Int](count: Int(max + 1), repeatedValue: 0) for num in inputList{ countArray[num] += 1 } print(countArray)