如何从类对象数组中提取指定属性的唯一值?
从对象数组的指定属性提取唯一值
好问题!确实,直接用Array(Set(myArray))这套玩法只适用于值类型的数组——比如你提到的[1,2,2,3]这种,因为每个元素都是独立的值。但对于Alert这种引用类型的对象数组,每个实例都是独一无二的,Set根本识别不出它们的属性重复,所以这套方法就失效了。下面给你几个实用的解决方案:
方法1:先抽属性再去重(最直观)
思路很简单:先把数组里每个Alert对象的目标属性提取出来,变成一个值类型数组,再用Set去重,最后转回数组就行。
因为你的Alert属性都是可选类型(比如category: String?),所以推荐用compactMap过滤掉nil值,避免出现[String?]这种包含空值的数组:
// 假设你有一个Alert数组 let alerts: [Alert] = // 你的数据源 // 提取category的唯一非空值 let uniqueCategories = Array(Set(alerts.compactMap { $0.category }))
如果你不想过滤nil,只是要保留可选值的唯一组合,把compactMap换成map就行:
let uniqueCategoryIds = Array(Set(alerts.map { $0.category_id }))
方法2:保持顺序的去重方案
上面的Set方法会打乱原数组中属性出现的顺序,如果你需要保留第一次出现的顺序,可以用reduce手动构建唯一数组:
let uniqueOrderedCategories = alerts.compactMap { $0.category }.reduce(into: [String]()) { result, category in if !result.contains(category) { result.append(category) } }
这个方法会严格按照属性在原数组中首次出现的顺序排列,适合需要保持时序的场景。
方法3:写个扩展让代码复用性拉满
如果以后经常要从不同对象数组提取属性的唯一值,不如给Array写个扩展,用KeyPath来指定属性,这样调用起来超方便:
extension Array { // 无序去重(过滤nil) func uniqueValues<T: Hashable>(for keyPath: KeyPath<Element, T?>) -> [T] { return Array(Set(compactMap { $0[keyPath: keyPath] })) } // 有序去重(过滤nil) func uniqueValuesOrdered<T: Equatable>(for keyPath: KeyPath<Element, T?>) -> [T] { return compactMap { $0[keyPath: keyPath] }.reduce(into: [T]()) { result, value in if !result.contains(value) { result.append(value) } } } }
使用的时候一行代码搞定:
let uniqueCategories = alerts.uniqueValues(for: \.category) let uniqueOrderedIds = alerts.uniqueValuesOrdered(for: \.category_id)
小提示
- 如果你的
Alert属性改成非可选类型(比如var category: String),可以把扩展里的T?改成T,compactMap换成map就行。 - 只有符合
Hashable协议的类型才能用Set去重,Swift自带的String、Int、Bool这些基本类型都没问题,自定义类型要支持的话记得实现Hashable协议。
内容的提问来源于stack exchange,提问作者CodeLikeBeaker
相关产品推荐
相关产品推荐

