如何计算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
的新版本,它使用inout
variables来累计结果。 利用这一点,计数的创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。 使用NSCountedSet
, map
方法并返回一个元组数组(需要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。 使用NSCountedSet
, AnyIterator
并返回一个元组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)