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



我已经看到了一些这样的例子,但所有这些似乎都依赖于知道要计算哪个元素的出现次数。我的数组是动态生成的,所以我无法知道我想要计算哪个元素的出现次数(我想要计算所有元素的出现次数)。有人能给点建议吗?

编辑:

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

在事先不知道foo, bar和foobar是什么的情况下,如何计算它们的出现次数?

迅速迅速3和2:

您可以使用[String: Int]类型的字典来建立[String]中每个项目的计数:

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)

迅速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变量来累积结果。使用它,计数的创建真正变成了一行:

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 }

最后,您可以将此扩展为Sequence,以便它可以在包含Hashable项的任何Sequence上调用,包括Array, ArraySlice, StringString.SubSequence:

extension Sequence where Element: Hashable {
    var histogram: [Element: Int] {
        return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 }
    }
}
这个想法是从这个问题借来的,尽管我把它改成了计算属性。感谢@LeoDabus建议扩展Sequence而不是Array以获取额外的类型。

例子:

print("abacab".histogram)
["a": 3, "b": 2, "c": 1]
print("Hello World!".suffix(6).histogram)
["l": 1, "!": 1, "d": 1, "o": 1, "W": 1, "r": 1]
print([1,2,3,2,1].histogram)
[2: 2, 3: 1, 1: 2]
print([1,2,3,2,1,2,1,3,4,5].prefix(8).histogram)
[1: 3, 2: 3, 3: 2]
print(stride(from: 1, through: 10, by: 2).histogram)
[1: 1, 3: 1, 5: 1, 7: 1, 9: 1]
array.filter{$0 == element}.count

在Swift 5中,根据你的需要,你可以选择下面的7个Playground示例代码中的一个来计算数组中可哈希项的出现次数。


# 1。使用Arrayreduce(into:_:)Dictionarysubscript(_:default:)下标

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

# 2。使用repeatElement(_:count:)函数、zip(_:_:)函数和Dictionaryinit(_:uniquingKeysWith:)初始化器

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。使用Dictionaryinit(grouping:by:)初始化器和mapValues(_:)方法

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

# 4。使用Dictionaryinit(grouping:by:)初始化器和map(_:)方法

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

# 5。使用for循环和Dictionarysubscript(_:)下标

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]

# 6。使用NSCountedSetNSEnumeratormap(_:)方法(需要基础)

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

# 7。使用NSCountedSetAnyIterator(需要基础)

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

学分:

  • 迅速习语
  • 通用的集合,使用字典

我更新了oisdk对Swift2的回答。

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

16/10/11更新为Swift3


Hashable:

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)

使用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)]

我喜欢避免内部循环,并尽可能使用。map。因此,如果我们有一个字符串数组,我们可以执行以下操作来计算

的出现次数
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]

Swift 4

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

另一种方法是使用filter方法。我发现最优雅的

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

您可以使用这个函数来计算数组

中条目的出现次数
func checkItemCount(arr: [String]) {       
    var dict = [String: Any]()
    for x in arr {  
        var count = 0 
        for y in arr {
            if y == x {
                count += 1
            }
        }
        dict[x] = count
    }
    print(dict)
}

你可以这样实现它-

let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
checkItemCount(arr: arr)
public extension Sequence {
    public func countBy<U : Hashable>(_ keyFunc: (Iterator.Element) -> U) -> [U: Int] {
    var dict: [U: Int] = [:]
    for el in self {
        let key = keyFunc(el)
        if dict[key] == nil {
            dict[key] = 1
        } else {
            dict[key] = dict[key]! + 1
        }
        //if case nil = dict[key]?.append(el) { dict[key] = [el] }
    }
    return dict
}

let count = ["a","b","c","a"].countBy{ $0 }
// ["b": 1, "a": 2, "c": 1]

struct Objc {
    var id: String = ""
}
let count = [Objc(id: "1"), Objc(id: "1"), Objc(id: "2"),Objc(id: "3")].countBy{ $0.id }
// ["2": 1, "1": 2, "3": 1]
extension Collection where Iterator.Element: Comparable & Hashable {
    func occurrencesOfElements() -> [Element: Int] {
        var counts: [Element: Int] = [:]
        let sortedArr = self.sorted(by: { $0 > $1 })
        let uniqueArr = Set(sortedArr)
        if uniqueArr.count < sortedArr.count {
            sortedArr.forEach {
                counts[$0, default: 0] += 1
            }
        }
        return counts
    }
}
// Testing with...
[6, 7, 4, 5, 6, 0, 6].occurrencesOfElements()
// Expected result (see number 6 occurs three times) :
// [7: 1, 4: 1, 5: 1, 6: 3, 0: 1]

计数排序的第一步。

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)

两个解决方案:

  1. 使用forEach循环
let array = [10,20,10,40,10,20,30]
var processedElements = [Int]()
array.forEach({
    let element = $0
    
    // Check wether element is processed or not
    guard processedElements.contains(element) == false else {
        return
    }
    let elementCount = array.filter({ $0 == element}).count
    print("Element: (element): Count (elementCount)")
    
    // Add Elements to already Processed Elements
    processedElements.append(element)
})
使用递归函数
let array = [10,20,10,40,10,20,30]
self.printElementsCount(array: array)
func printElementsCount(array: [Int]) {
    guard array.count > 0 else {
        return
    }
    let firstElement = array[0]
    let filteredArray = array.filter({ $0 != firstElement })
    print("Element: (firstElement): Count (array.count - filteredArray.count )")
    printElementsCount(array: filteredArray)
}
import Foundation
var myArray:[Int] = []
for _ in stride(from: 0, to: 10, by: 1) {
    myArray.append(Int.random(in: 1..<6))
}
// Method 1:
var myUniqueElements = Set(myArray)
print("Array: (myArray)")
print("Unique Elements: (myUniqueElements)")
for uniqueElement in myUniqueElements {
    var quantity = 0
    for element in myArray {
        if element == uniqueElement {
            quantity += 1
        }
    }
    print("Element: (uniqueElement), Quantity: (quantity)")
}
// Method 2:
var myDict:[Int:Int] = [:]
for element in myArray {
    myDict[element] = (myDict[element] ?? 0) + 1
}
print(myArray)
for keyValue in myDict {
    print("Element: (keyValue.key), Quantity: (keyValue.value)")
}

执行计数的结构

struct OccureCounter<Item: Hashable> {
    var dictionary = [Item: Int]()
    mutating func countHere(_ c: [Item]) {
        c.forEach { add(item: $0) }
        printCounts()
    }
    mutating func add(item: Item) {
        if let value = dictionary[item] {
            dictionary[item] = value + 1
        } else {
            dictionary[item] = 1
        }
    }
    func printCounts() {
        print("::: START")
        dictionary
            .sorted { $0.value > $1.value }
            .forEach { print("::: ($0.value) — ($0.key)") }
    
        let all = dictionary.reduce(into: 0) { $0 += $1.value }
        print("::: ALL: (all)")
        print("::: END")
    }
}
使用

struct OccureTest {
    func test() {
        let z: [Character] = ["a", "a", "b", "a", "b", "c", "d", "e", "f"]
        var counter = OccureCounter<Character>()
        counter.countHere(z)
    }
}

它打印:

::: START
::: 3 — a
::: 2 — b
::: 1 — c
::: 1 — f
::: 1 — e
::: 1 — d
::: ALL: 9
::: END

最新更新