受https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift 的启发,我们可以声明一个更强大的工具,它能够过滤任何keyPath 上的唯一性。感谢 Alexander cmets 关于复杂性的各种答案,以下解决方案应该接近最优。
非变异解决方案
我们扩展了一个能够过滤任何 keyPath 上的唯一性的函数:
extension RangeReplaceableCollection {
/// Returns a collection containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> Self {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}
注意:如果你的对象不符合 RangeReplaceableCollection,但符合 Sequence,你可以有这个额外的扩展,但返回类型总是一个数组:
extension Sequence {
/// Returns an array containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> [Element] {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}
用法
如果我们想要元素本身的唯一性,就像问题一样,我们使用 keyPath \.self:
let a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let b = a.unique(for: \.self)
/* b is [1, 4, 2, 6, 24, 15, 60] */
如果我们想要其他东西的唯一性(例如对象集合的id),那么我们使用我们选择的 keyPath:
let a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
let b = a.unique(for: \.y)
/* b is [{x 1 y 1}, {x 1 y 2}] */
变异解决方案
我们扩展了一个变异函数,该函数能够过滤任何 keyPath 上的唯一性:
extension RangeReplaceableCollection {
/// Keeps only, in order, the first instances of
/// elements of the collection that compare equally for the keyPath.
mutating func uniqueInPlace<T: Hashable>(for keyPath: KeyPath<Element, T>) {
var unique = Set<T>()
removeAll { !unique.insert($0[keyPath: keyPath]).inserted }
}
}
用法
如果我们想要元素本身的唯一性,就像问题一样,我们使用 keyPath \.self:
var a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
a.uniqueInPlace(for: \.self)
/* a is [1, 4, 2, 6, 24, 15, 60] */
如果我们想要其他东西的唯一性(比如对象集合的id),那么我们使用我们选择的 keyPath:
var a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
a.uniqueInPlace(for: \.y)
/* a is [{x 1 y 1}, {x 1 y 2}] */