ios 如何计算 Swift 数组中某个元素的出现次数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30545518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:08:41  来源:igfitidea点击:

How to count occurrences of an element in a Swift array?

iosarraysswiftnsarray

提问by Alex Chesters

I've seen a few examples of this but all of those seem to rely on knowing which element you want to count the occurrences of. My array is generated dynamically so I have no way of knowing which element I want to count the occurrences of (I want to count the occurrences of all of them). Can anyone advise?

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

Thanks in advance

提前致谢

EDIT:

编辑:

Perhaps I should have been clearer, the array will contain multiple different strings (e.g. ["FOO", "FOO", "BAR", "FOOBAR"]

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

How can I count the occurrences of foo, bar and foobar without knowing what they are in advance?

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

回答by vacawama

Swift 3 and Swift 2:

斯威夫特 3 和斯威夫特 2:

You can use a dictionary of type [String: Int]to build up counts for each of the items in your [String]:

您可以使用类型字典为您的 中的[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)")
}

output:

输出:

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


Swift 4:

斯威夫特 4:

Swift 4 introduces (SE-0165)the ability to include a default value with a dictionary lookup, and the resulting value can be mutated with operations such as +=and -=, so:

Swift 4引入了 (SE-0165)在字典查找中包含默认值的能力,并且结果值可以使用+=and等操作进行变异-=,因此:

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

becomes:

变成:

counts[item, default: 0] += 1

That makes it easy to do the counting operation in one concise line using forEach:

这使得使用forEach以下命令在一行简洁的代码中轻松完成计数操作:

let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
var counts: [String: Int] = [:]

arr.forEach { counts[
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]
, default: 0] += 1 } print(counts) // "["FOOBAR": 1, "FOO": 2, "BAR": 1]"


Swift 4: reduce(into:_:)

斯威夫特 4: reduce(into:_:)

Swift 4 introduces a new version of reducethat uses an inoutvariable to accumulate the results. Using that, the creation of the counts truly becomes a single line:

Swift 4 引入了一个新版本,reduce它使用inout变量来累积结果。使用它,计数的创建真正成为一行:

let counts = arr.reduce(into: [:]) { 
extension Array where Element: Hashable {
    var histogram: [Element: Int] {
        return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 }
    }
}
[, default: 0] += 1 }

Or using the default parameters:

或者使用默认参数:

array.filter{
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]
== element}.count

Finally you can make this an extension of Arrayso that it can be called on any array containing Hashableitems:

最后,您可以将其作为 的扩展,Array以便可以在任何包含Hashable项目的数组上调用它:

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]

This idea was borrowed from this questionalthough I changed it to a computed property.

这个想法是从这个问题借来的,尽管我把它改成了一个计算属性

回答by Ruben

let array = [4, 23, 97, 97, 97, 23]

let dictionary = Dictionary(grouping: array, by: { 
let array = [4, 23, 97, 97, 97, 23]

let dictionary = Dictionary(grouping: array, by: { 
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]
}) let newArray = dictionary.map { (key: Int, value: [Int]) in return (key, value.count) } print(newArray) // prints: [(4, 1), (23, 2), (97, 3)]
}) let newDictionary = dictionary.mapValues { (value: [Int]) in return value.count } print(newDictionary) // prints: [97: 3, 23: 2, 4: 1]

回答by Imanou Petit

With Swift 5, according to your needs, you may choose one of the 7 following Playground sample codesto count the occurrences of hashable items in an array.

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



#1. Using Array's reduce(into:_:)and Dictionary's subscript(_:default:)subscript

#1. 使用Array'sreduce(into:_:)Dictionary'ssubscript(_:default:)下标

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


#2. Using repeatElement(_:count:)function, zip(_:_:)function and Dictionary's init(_:uniquingKeysWith:)initializer

#2. 使用repeatElement(_:count:)function、zip(_:_:)function 和Dictionaryinit(_:uniquingKeysWith:)初始化程序

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


#3. Using a Dictionary's init(grouping:by:)initializer and mapValues(_:)method

#3. 使用 aDictionaryinit(grouping:by:)初始化程序和mapValues(_:)方法

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
        }
    }
}


#4. Using a Dictionary's init(grouping:by:)initializer and map(_:)method

#4. 使用 aDictionaryinit(grouping:by:)初始化程序和map(_:)方法

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


#5. Using a for loop and Dictionary's subscript(_:)subscript

#5. 使用 for 循环 andDictionarysubscript(_:)下标

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


#6. Using NSCountedSetand NSEnumerator's map(_:)method (requires Foundation)

#6. 使用NSCountedSetandNSEnumeratormap(_:)方法(需要 Foundation)

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)


#7. Using NSCountedSetand AnyIterator(requires Foundation)

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

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


Credits:

学分:

回答by ken0nek

I updated oisdk's answerto Swift2.

我更新了oisdk 对 Swift2 的回答

16/04/14 I updated this code to Swift2.2

16/04/14 我将此代码更新为 Swift2.2

16/10/11 updated to Swift3

16/10/11 更新到 Swift3



Hashable:

可哈希:

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]


Equatable:

等价:

freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3]

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


Usage

用法

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


var occurances = ["tuples", "are", "awesome", "tuples", "are", "cool", "tuples", "tuples", "tuples", "shades"]

var dict:[String:Int] = [:]

occurances.map{
    if let val: Int = dict[
["tuples": 5, "awesome": 1, "are": 2, "cool": 1, "shades": 1]
] { dict[
var numberOfOccurenses = countedItems.filter(
{
    if 
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]
== "FOO" ||
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{ ##代码## }
// ["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{ ##代码##.id }

// ["2": 1, "1": 2, "3": 1]
== "BAR" || ##代码## == "FOOBAR" { return true }else{ return false } }).count
] = val+1 } else { dict[##代码##] = 1 } }

回答by gnasher729

Use an NSCountedSet. In Objective-C:

使用 NSCountedSet。在 Objective-C 中:

##代码##

I assume that you can translate this into Swift yourself.

我假设您可以自己将其翻译成 Swift。

回答by oisdk

How about:

怎么样:

##代码##

It's generic, so it'll work with whatever your element is, as long as it's hashable:

它是通用的,所以它可以处理你的任何元素,只要它是可散列的:

##代码##

And, if you can't make your elements hashable, you could do it with tuples:

而且,如果你不能让你的元素可散列,你可以用元组来做:

##代码##

回答by EmilDo

I like to avoid inner loops and use .map as much as possible. So if we have an array of string, we can do the following to count the occurrences

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

##代码##

prints

印刷

##代码##

回答by user1700737

An other approach would be to use the filter method. I find that the most elegant

另一种方法是使用过滤器方法。我觉得最优雅

##代码##

回答by ViciV

Swift 4

斯威夫特 4

##代码##

回答by Carlos Chaguendo

##代码##