Swift 4 - 如何从数组中返回重复值的计数?

3
我有一个包含多个值(Double)的数组,其中许多是重复的。我想返回或打印所有唯一值的列表,并附带给定值在数组中出现次数的计数。 我对Swift还比较陌生,尝试了几种不同的方法,但不确定最好的方法是什么。
类似于这样: [65.0, 65.0, 65.0, 55.5, 55.5, 30.25, 30.25, 27.5]
将会打印(例如): “65.0出现3次,55.5出现2次,30.25出现2次,27.5出现1次。”
我真正关心的不是输出,而是实现这一目标的方法。
谢谢!

如果您不介意使用Foundation框架,可以了解一下NSCountedSet类。 - rmaddy
2个回答

18

如@rmaddy已经评论过的那样,您可以按照以下方式使用FoundationNSCountedSet

import Foundation // or iOS UIKit or macOS Cocoa

let values = [65.0, 65.0, 65.0, 55.5, 55.5, 30.25, 30.25, 27.5]
let countedSet = NSCountedSet(array: values)
print(countedSet.count(for: 65.0))   // 3
for value in countedSet {
    print("Element:", value, "count:", countedSet.count(for: value))
}

Xcode 11 • Swift 5.1

你也可以扩展NSCountedSet来返回一组元组或一个字典:

extension NSCountedSet {
    var occurences: [(object: Any, count: Int)] { map { ($0, count(for: $0))} }
    var dictionary: [AnyHashable: Int] {
        reduce(into: [:]) {
            guard let key = $1 as? AnyHashable else { return }
            $0[key] = count(for: key)
        }
    }
}

let values = [65.0, 65.0, 65.0, 55.5, 55.5, 30.25, 30.25, 27.5]
let countedSet = NSCountedSet(array: values)
for (key, value) in countedSet.dictionary {
    print("Element:", key, "count:", value)
}

对于一个纯Swift的解决方案,我们可以扩展Sequence并将其元素限制为Hashable

extension Sequence where Element: Hashable {
    var frequency: [Element: Int] { reduce(into: [:]) { $0[$1, default: 0] += 1 } }
}

let values = [65.0, 65.0, 65.0, 55.5, 55.5, 30.25, 30.25, 27.5]
let frequency = values.frequency
frequency[65] // 3
for (key, value) in frequency {
    print("Element:", key, "count:", value)
}

这些将被打印

Element: 27.5 count: 1
Element: 30.25 count: 2
Element: 55.5 count: 2
Element: 65 count: 3

如果您有一个自定义结构的集合,我们可以创建一个通用的方法,并使用关键路径语法如下:

如果您拥有自定义结构的集合,我们可以创建一个通用方法并使用关键路径语法,如下所示:

extension Sequence {
    func sum<T: Hashable>(of property: (Element) -> T) -> [T: Int] {
        reduce(into: [:]) { $0[property($1), default: 0] += 1 }
    }
    func sum<T: Hashable, A: AdditiveArithmetic>(of property: (Element) -> T, by adding: (Element) -> A) -> [T: A] {
        reduce(into: [:]) { $0[property($1), default: .zero] += adding($1) }
    }
}

使用方法:
struct Product {
    let id: String
    let name: String
    let quantity: Int
    let price: Decimal
    let total: Decimal
}

let products = [
    ("1", "iPhone",      2,  Decimal(string: "499.99")!,  Decimal(string: "999.98")!),
    ("2", "MacBook Pro", 1, Decimal(string: "2499.99")!, Decimal(string: "2499.99")!),
    ("3", "iPhone",      3, Decimal(string: "1199.99")!, Decimal(string: "3599.97")!),
    ("4", "iPhone",      1,  Decimal(string: "999.99")!,  Decimal(string: "999.99")!),
    ("5", "MacBook Pro", 2, Decimal(string: "1499.99")!, Decimal(string: "2999.98")!)
].map(Product.init)
 
let sum1 = products.sum(of: \.name)
sum1["iPhone"]       // 3
sum1["MacBook Pro"]  // 2

let sum2 = products.sum(of: \.name, by: \.quantity)
sum2["iPhone"]       // 6
sum2["MacBook Pro"]  // 3

let sum3 = products.sum(of: \.name, by: \.total)
sum3["iPhone"]       // 5599.94
sum3["MacBook Pro"]  // 5499.97

5
你可以遍历数组并将值添加到字典中。
var array: [CGFloat] =  [65.0, 65.0, 65.0, 55.5, 55.5, 30.25, 30.25, 27.5]
var dictionary = [CGFloat: Int]()

for item in array {
   dictionary[item] = dictionary[item] ?? 0 + 1
}

print(dictionary)

或者您可以在数组上进行foreach循环:
array.forEach { (item) in
  dictionary[item] = dictionary[item] ?? 0 + 1
}

print(dictionary)

或者如@rmaddy所说:

var set: NSCountedSet =  [65.0, 65.0, 65.0, 55.5, 55.5, 30.25, 30.25, 27.5]
var dictionary = [Float: Int]()
set.forEach { (item) in
  dictionary[item as! Float] = set.count(for: item)
}

print(dictionary)

可以用单个语句 dictionary[item] = dictionary[item] ?? 0 + 1 替换 if 语句。更好的方法是,直接使用 CountedSet :) - David Berry
@DavidBerry,我已经更新了我的答案,谢谢。 - Mina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接