我可能有一个像下面这样的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似类型的数据部分的序列。我要做的是确保每个相同的元素只有一个。例如,上面的数组将变成:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?
我可能有一个像下面这样的数组:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
或者,实际上,任何类似类型的数据部分的序列。我要做的是确保每个相同的元素只有一个。例如,上面的数组将变成:
[1, 4, 2, 6, 24, 15, 60]
请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?
当前回答
你可以自己卷,比如这样:
func unique<S : Sequence, T : Hashable>(source: S) -> [T] where S.Iterator.Element == T {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(vals) // [1, 4, 2, 6, 24, 15, 60]
作为Array的扩展:
extension Array where Element: Hashable {
func uniqued() -> Array {
var buffer = Array()
var added = Set<Element>()
for elem in self {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
}
或者更优雅一点(Swift 4/5):
extension Sequence where Element: Hashable {
func uniqued() -> [Element] {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
将被使用:
[1,2,4,2,1].uniqued() // => [1,2,4]
其他回答
受https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift的启发,我们可以声明一个更强大的工具,它能够过滤任何keyPath上的唯一性。感谢Alexander关于复杂性的各种答案的评论,下面的解决方案应该是最优的。
Non-mutating解决方案
我们扩展了一个函数,它可以过滤任意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}] */
从数组中删除重复项的简单方法
extension Array where Element: Equatable {
mutating func removeDuplicates() {
var result = [Element]()
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}}
您总是可以使用Dictionary,因为Dictionary只能保存惟一的值。例如:
var arrayOfDates: NSArray = ["15/04/01","15/04/01","15/04/02","15/04/02","15/04/03","15/04/03","15/04/03"]
var datesOnlyDict = NSMutableDictionary()
var x = Int()
for (x=0;x<(arrayOfDates.count);x++) {
let date = arrayOfDates[x] as String
datesOnlyDict.setValue("foo", forKey: date)
}
let uniqueDatesArray: NSArray = datesOnlyDict.allKeys // uniqueDatesArray = ["15/04/01", "15/04/03", "15/04/02"]
println(uniqueDatesArray.count) // = 3
正如你所看到的,生成的数组并不总是按“顺序”排列。如果你想对数组排序,添加这个:
var sortedArray = sorted(datesOnlyArray) {
(obj1, obj2) in
let p1 = obj1 as String
let p2 = obj2 as String
return p1 < p2
}
println(sortedArray) // = ["15/04/01", "15/04/02", "15/04/03"]
.
斯威夫特2
用uniq函数回答:
func uniq<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter({ (v) -> Bool in
return seen.updateValue(true, forKey: v) == nil
})
}
use:
var test = [1,2,3,4,5,6,7,8,9,9,9,9,9,9]
print(uniq(test)) //1,2,3,4,5,6,7,8,9
Daniel Krom的Swift 2答案的更简洁的语法版本,使用了一个尾随闭包和简写参数名,这似乎是基于Airspeed Velocity的原始答案:
func uniq<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {
var seen = [E: Bool]()
return source.filter { seen.updateValue(true, forKey: $0) == nil }
}
实现一个可以与uniq(_:)一起使用的自定义类型的示例(必须符合Hashable,因此符合Equatable,因为Hashable扩展了Equatable):
func ==(lhs: SomeCustomType, rhs: SomeCustomType) -> Bool {
return lhs.id == rhs.id // && lhs.someOtherEquatableProperty == rhs.someOtherEquatableProperty
}
struct SomeCustomType {
let id: Int
// ...
}
extension SomeCustomType: Hashable {
var hashValue: Int {
return id
}
}
在上面的代码中…
在==重载中使用的id可以是任何Equatable类型(或返回Equatable类型的方法,例如someMethodThatReturnsAnEquatableType())。注释掉的代码演示了扩展相等性检查,其中someOtherEquatableProperty是Equatable类型的另一个属性(但也可以是返回Equatable类型的方法)。
在hashValue计算属性中使用的id(必须符合Hashable)可以是任何Hashable(因此是Equatable)属性(或返回Hashable类型的方法)。
使用uniq(_:)的示例:
var someCustomTypes = [SomeCustomType(id: 1), SomeCustomType(id: 2), SomeCustomType(id: 3), SomeCustomType(id: 1)]
print(someCustomTypes.count) // 4
someCustomTypes = uniq(someCustomTypes)
print(someCustomTypes.count) // 3