从Swift中删除数组中的重复元素

Alt*_*357 222 arrays standard-library swift

我可能有一个如下所示的数组:

[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]

或者,实际上,任何类似数据类型的序列.我想要做的是确保每个相同的元素只有一个.例如,上面的数组将变为:

[1, 4, 2, 6, 24, 15, 60]

请注意,删除了重复的2,6和15,以确保每个相同的元素只有一个.Swift是否提供了一种轻松完成此操作的方法,或者我自己必须这样做?

Ben*_*ard 452

您可以很容易地再次转换为集合并返回到数组:

let unique = Array(Set(originals))
Run Code Online (Sandbox Code Playgroud)

这不能保证维持数组的原始顺序.

  • 有没有办法在保留数组原始顺序的同时使用集合? (34认同)
  • 如果“originals”中的元素不是“Hashable”,则失败;只有“Hashable”数据类型可以添加到 Set 中,但任何数据类型都可以添加到数组中。 (8认同)
  • @Crashalot看到我的回答. (6认同)
  • 如果您需要通过特定属性保持对象唯一,那么还要在该类上实现Hashable和Equatable协议,而不是仅使用Array-> Set-> Array转换 (5认同)
  • 我不明白为什么这个答案有这么多赞成票。似乎维护数组的顺序几乎肯定是一个要求。否则,您最好一开始就使用 Set 而不是 Array。 (4认同)
  • 好的!!请问这个解决方案的时间复杂度是多少? (2认同)

Jea*_*let 114

你可以自己滚动,例如像这样(使用Set更新为Swift 1.2):

func uniq<S : SequenceType, T : Hashable where S.Generator.Element == T>(source: S) -> [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]
Run Code Online (Sandbox Code Playgroud)

Swift 3版本:

func uniq<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
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意:在您可靠地依赖其性能之前,请避免讨论此类简单函数的性能,此时您应该做的唯一事情就是基准测试.由于做出假设,我经常看到不可维护的代码或性能更低的代码.:)此外,这可能更容易理解:`let uniques = Array(Set(vals))` (16认同)
  • 您还可以将该函数的主体实现为`var addedDict = [T:Bool](); return filter(source){addedDict(true,forKey:$ 0)== nil}` (10认同)
  • @Blixt同意.再一次,这里的优点在于尊重原始数组的元素的顺序. (7认同)

Ant*_*ine 66

这里有很多答案,但我错过了这个简单的扩展,适用于Swift 2及以上版本:

extension Array where Element:Equatable {
    func removeDuplicates() -> [Element] {
        var result = [Element]()

        for value in self {
            if result.contains(value) == false {
                result.append(value)
            }
        }

        return result
    }
}
Run Code Online (Sandbox Code Playgroud)

使它变得非常简单.可以像这样调用:

let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]
Run Code Online (Sandbox Code Playgroud)

根据属性进行过滤

要根据属性筛选数组,可以使用此方法:

extension Array {

    func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
        var results = [Element]()

        forEach { (element) in
            let existingElements = results.filter {
                return includeElement(lhs: element, rhs: $0)
            }
            if existingElements.count == 0 {
                results.append(element)
            }
        }

        return results
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以按以下方式拨打电话:

let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
Run Code Online (Sandbox Code Playgroud)

  • 这将具有“ O(n²)”的时间性能,这对于大型阵列来说确实很糟糕。 (5认同)
  • 你应该使用一个集合来跟踪到目前为止看到的元素,将这种可怕的“O(n²)”复杂度降低到“O(n)” (2认同)

Jes*_*ssy 57

这会获取本页面上已有的一些好信息,并在可能的情况下应用Hashable/Set方法,否则将回退到Equatable代码.

Swift 4更改Hashable扩展名(Equatable保持不变)

public extension Sequence where Element: Hashable {
  var firstUniqueElements: [Element] {
    var set: Set<Element> = []
    return filter { set.insert($0).inserted }
  }
}

public extension Sequence where Element: Equatable {
  var firstUniqueElements: [Element] {
    reduce(into: []) { uniqueElements, element in
      if !uniqueElements.contains(element) {
        uniqueElements.append(element)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

public extension Sequence where Element: Hashable {
  var firstUniqueElements: [Element] {
    var set: Set<Element> = []
    return filter { set.insert($0).inserted }
  }
}

public extension Sequence where Element: Equatable {
  var firstUniqueElements: [Element] {
    reduce(into: []) { uniqueElements, element in
      if !uniqueElements.contains(element) {
        uniqueElements.append(element)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特2

public extension Sequence where Element: Hashable {
  var firstUniqueElements: [Element] {
    var set: Set<Element> = []
    return filter { set.insert($0).inserted }
  }
}

public extension Sequence where Element: Equatable {
  var firstUniqueElements: [Element] {
    reduce(into: []) { uniqueElements, element in
      if !uniqueElements.contains(element) {
        uniqueElements.append(element)
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 愚蠢的问题,但这怎么称呼?:/ (2认同)
  • 这将具有 O(n²) 的时间性能,这对于大型数组来说真的很糟糕。 (2认同)

Jov*_*vic 56

Swift 3.0

let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
Run Code Online (Sandbox Code Playgroud)

  • `Array(NSOrderedSet(array: array))` 在 Swift 5 中不起作用。使用 `NSOrderedSet(array: array).array 作为![字符串]` 代替。 (3认同)
  • 让 uniqueOrderedNames = Array(NSOrderedSet(array: userNames)) as! [String] 如果你有 String 数组,而不是 Any (2认同)

Leo*_*bus 42

将集合元素约束为Equatable,您可以使用contains:

extension RangeReplaceableCollection where Element: Hashable {
    var orderedSet: Self {
        var set = Set<Element>()
        return filter { set.insert($0).inserted }
    }
    mutating func removeDuplicates() {
        var set = Set<Element>()
        removeAll { !set.insert($0).inserted }
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是将集合元素约束为Hashable,并使用集合来控制必须映射到结果中的元素:

let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Run Code Online (Sandbox Code Playgroud)

使用过滤器:

"abcdefabcghi".orderedSet  // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"
Run Code Online (Sandbox Code Playgroud)

或使用NSOrderedSet:

var string = "abcdefabcghi"
string.removeDuplicates() 
string  //  "abcdefghi"

var substring = "abcdefabcdefghi".dropFirst(3)  // "defabcdefghi"
substring.removeDuplicates()
substring   // "defabcghi"
Run Code Online (Sandbox Code Playgroud)

使用Swift 4减少(进:)

extension RangeReplaceableCollection where Element: Hashable {
    var orderedSet: Self {
        var set = Set<Element>()
        return filter { set.insert($0).inserted }
    }
    mutating func removeDuplicates() {
        var set = Set<Element>()
        removeAll { !set.insert($0).inserted }
    }
}
Run Code Online (Sandbox Code Playgroud)
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Run Code Online (Sandbox Code Playgroud)

您还可以扩展RangeReplaceableCollection协议以允许它与StringProtocol类型一起使用(字符串和子串):

"abcdefabcghi".orderedSet  // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"
Run Code Online (Sandbox Code Playgroud)
var string = "abcdefabcghi"
string.removeDuplicates() 
string  //  "abcdefghi"

var substring = "abcdefabcdefghi".dropFirst(3)  // "defabcdefghi"
substring.removeDuplicates()
substring   // "defabcghi"
Run Code Online (Sandbox Code Playgroud)

变异方法

extension RangeReplaceableCollection where Element: Hashable {
    var orderedSet: Self {
        var set = Set<Element>()
        return filter { set.insert($0).inserted }
    }
    mutating func removeDuplicates() {
        var set = Set<Element>()
        removeAll { !set.insert($0).inserted }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • O(N ^ 2)很糟糕:( (6认同)
  • @Alexander Leo Dabus 已经替换了 `reduce` 实现,所以现在复杂度不同了。 (2认同)

mxc*_*xcl 33

斯威夫特4

public extension Array where Element: Hashable {
    func uniqued() -> [Element] {
        var seen = Set<Element>()
        return filter{ seen.insert($0).inserted }
    }
}
Run Code Online (Sandbox Code Playgroud)

每一次尝试insert都会返回一个元组:(inserted: Bool, memberAfterInsert: Set.Element).见文档.

使用返回的值有助于我们避免循环或执行任何其他操作.

  • 简单分析后,这种方法非常快.比使用reduce(_:_ :),甚至减少(进入:_ :)快几百倍 (7认同)
  • @Kelvin因为所有其他算法都是O(n ^ 2),没有人注意到。 (2认同)
  • 请注意,口是心非是由两个对象/结构的哈希值决定的,而不是“Equatable”协议中的“==”相等性。如果你想确定结构中的口是心非,比如仅在某些属性(不是所有属性)上,你需要使用自定义 Hashable 实现和 `func hash(into hasher: inout Hasher)` (2认同)

Ale*_*tin 28

斯威夫特4

保证继续订购.

extension Array where Element: Equatable {
    func removingDuplicates() -> Array {
        return reduce(into: []) { result, element in
            if !result.contains(element) {
                result.append(element)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这将具有“ O(n²)”的时间性能,这对于大型阵列来说确实很糟糕。 (3认同)

Kau*_*eya 24

无需编写任何扩展。

uniqued()苹果终于在其算法包中引入了方法。

需要考虑的要点:

  1. 不仅仅是数组。它可以用于任何符合Sequence协议的类型。
  2. Sequence中的Element类型必须符合Hashable协议

例子:

import Algorithms

let numbers = [1, 2, 3, 3, 2, 3, 3, 2, 2, 2, 1]
print(numbers.uniqued()) // prints [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

更多信息https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md


dea*_*rne 16

这是一个SequenceType保留数组原始顺序的类别,但使用a Set进行contains查找以避免O(n)Array contains(_:)方法的成本.

public extension Array where Element: Hashable {

    /// Return the array with all duplicates removed.
    ///
    /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234, as 
    ///         per @Alexander's comment.
    public func uniqued() -> [Element] {
        var seen = Set<Element>()
        return self.filter { seen.insert($0).inserted }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者如果你没有Hashable,你可以这样做:

public extension Sequence where Iterator.Element: Equatable {

    public func uniqued() -> [Iterator.Element] {
        var buffer: [Iterator.Element] = []

        for element in self {
            guard !buffer.contains(element) else { continue }

            buffer.append(element)
        }

        return buffer
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以将这两者都粘贴到您的应用程序中,Swift将根据您的序列Iterator.Element类型选择正确的一个.


Pli*_*kin 12

一个替代(如果不是最优的)从溶液这里使用不可改变的类型,而不是变量:

func deleteDuplicates<S: ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
    let s = reduce(seq, S()){
        ac, x in contains(ac,x) ? ac : ac + [x]
    }
    return s
}
Run Code Online (Sandbox Code Playgroud)

包括将Jea​​n-Pillippe的强制性方法与功能性方法进行对比.

作为奖励,此功能适用于字符串和数组!

编辑:这个答案是2014年为Swift 1.0编写的(以前Set在Swift中可用).它不需要Hashable一致性并且在二次时间内运行.

  • 要注意,没有一种,但是有两种方式在二次时间内运行 - "包含"和数组附加在O(n)中运行.虽然它确实具有仅需要等同的,不可清洗的好处. (8认同)

Tim*_* MB 12

像函数式程序员一样思考 :)

要根据元素是否已经出现来过滤列表,您需要索引。您可以使用enumerated获取索引并map返回值列表。

let unique = myArray
    .enumerated()
    .filter{ myArray.firstIndex(of: $0.1) == $0.0 }
    .map{ $0.1 }
Run Code Online (Sandbox Code Playgroud)

这保证了顺序。如果您不介意顺序,那么现有的答案Array(Set(myArray))更简单,可能更有效。


更新:关于效率和正确性的一些说明

一些人对效率发表了评论。我绝对是在先编写正确和简单的代码,然后再找出瓶颈的学校,尽管我很欣赏这是否比Array(Set(array)).

这种方法比Array(Set(array)). 正如评论中所指出的,它确实保留了顺序并适用于不可 Hashable 的元素。

但是,@Alain T 的方法也保留了顺序,而且速度也快了很多。因此,除非您的元素类型不可散列,或者您只需要一个快速的班轮,否则我建议使用他们的解决方案。

以下是在 Xcode 11.3.1 (Swift 5.1) 上的 MacBook Pro (2014) 发布模式下的一些测试。

profiler函数和两种比较方法:

func printTimeElapsed(title:String, operation:()->()) {
    var totalTime = 0.0
    for _ in (0..<1000) {
        let startTime = CFAbsoluteTimeGetCurrent()
        operation()
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        totalTime += timeElapsed
    }
    let meanTime = totalTime / 1000
    print("Mean time for \(title): \(meanTime) s")
}

func method1<T: Hashable>(_ array: Array<T>) -> Array<T> {
    return Array(Set(array))
}

func method2<T: Equatable>(_ array: Array<T>) -> Array<T>{
    return array
    .enumerated()
    .filter{ array.firstIndex(of: $0.1) == $0.0 }
    .map{ $0.1 }
}

// Alain T.'s answer (adapted)
func method3<T: Hashable>(_ array: Array<T>) -> Array<T> {
    var uniqueKeys = Set<T>()
    return array.filter{uniqueKeys.insert($0).inserted}
}
Run Code Online (Sandbox Code Playgroud)

以及少量的测试输入:

func randomString(_ length: Int) -> String {
  let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  return String((0..<length).map{ _ in letters.randomElement()! })
}

let shortIntList = (0..<100).map{_ in Int.random(in: 0..<100) }
let longIntList = (0..<10000).map{_ in Int.random(in: 0..<10000) }
let longIntListManyRepetitions = (0..<10000).map{_ in Int.random(in: 0..<100) }
let longStringList = (0..<10000).map{_ in randomString(1000)}
let longMegaStringList = (0..<10000).map{_ in randomString(10000)}
Run Code Online (Sandbox Code Playgroud)

作为输出给出:

Mean time for method1 on shortIntList: 2.7358531951904296e-06 s
Mean time for method2 on shortIntList: 4.910230636596679e-06 s
Mean time for method3 on shortIntList: 6.417632102966309e-06 s
Mean time for method1 on longIntList: 0.0002518167495727539 s
Mean time for method2 on longIntList: 0.021718120217323302 s
Mean time for method3 on longIntList: 0.0005312927961349487 s
Mean time for method1 on longIntListManyRepetitions: 0.00014377200603485108 s
Mean time for method2 on longIntListManyRepetitions: 0.0007293639183044434 s
Mean time for method3 on longIntListManyRepetitions: 0.0001843773126602173 s
Mean time for method1 on longStringList: 0.007168249964714051 s
Mean time for method2 on longStringList: 0.9114790915250778 s
Mean time for method3 on longStringList: 0.015888616919517515 s
Mean time for method1 on longMegaStringList: 0.0525397013425827 s
Mean time for method2 on longMegaStringList: 1.111266262292862 s
Mean time for method3 on longMegaStringList: 0.11214958941936493 s
Run Code Online (Sandbox Code Playgroud)

  • ...与“Array(Set(myArray))”不同的是,数组的顺序是维持的。 (2认同)

bla*_*acx 11

斯威夫特 5

extension Sequence where Element: Hashable {
    func unique() -> [Element] {
        NSOrderedSet(array: self as! [Any]).array as! [Element]
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*rom 10

迅捷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
    })
}
Run Code Online (Sandbox Code Playgroud)

使用:

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
Run Code Online (Sandbox Code Playgroud)


小智 10

在斯威夫特 5

 var array: [String] =  ["Aman", "Sumit", "Aman", "Sumit", "Mohan", "Mohan", "Amit"]

 let uniq = Array(Set(array))
 print(uniq)
Run Code Online (Sandbox Code Playgroud)

输出将是

 ["Sumit", "Mohan", "Amit", "Aman"]
Run Code Online (Sandbox Code Playgroud)

  • 这是对此处已有许多答案的重复,并且不保留顺序。 (2认同)

Rok*_*rič 9

Swift 4.x:

extension Sequence where Iterator.Element: Hashable {
  func unique() -> [Iterator.Element] {
    return Array(Set<Iterator.Element>(self))
  }

  func uniqueOrdered() -> [Iterator.Element] {
    return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()
Run Code Online (Sandbox Code Playgroud)

要么

["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
Run Code Online (Sandbox Code Playgroud)


Ene*_*nso 8

还有一个Swift 3.0解决方案可以从阵列中删除重复项.此解决方案改进了已提出的许多其他解决方案:

  • 保留输入数组中元素的顺序
  • 线性复杂度O(n):单通滤波器O(n)+集插入O(1)

给定整数数组:

let numberArray = [10, 1, 2, 3, 2, 1, 15, 4, 5, 6, 7, 3, 2, 12, 2, 5, 5, 6, 10, 7, 8, 3, 3, 45, 5, 15, 6, 7, 8, 7]
Run Code Online (Sandbox Code Playgroud)

功能代码:

func orderedSet<T: Hashable>(array: Array<T>) -> Array<T> {
    var unique = Set<T>()
    return array.filter { element in
        return unique.insert(element).inserted
    }
}

orderedSet(array: numberArray)  // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
Run Code Online (Sandbox Code Playgroud)

数组扩展代码:

extension Array where Element:Hashable {
    var orderedSet: Array {
        var unique = Set<Element>()
        return filter { element in
            return unique.insert(element).inserted
        }
    }
}

numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
Run Code Online (Sandbox Code Playgroud)

此代码利用insert操作返回的结果,该结果Set执行O(1),并返回一个元组,指示项目是否已插入或是否已存在于集合中.

如果该项目在集合中,filter则会将其从最终结果中排除.

  • 尼斯.你的扩展可能是最好的,在`扩展序列,其中Iterator.Element:Hashable {...}` (2认同)
  • @阿兰T。没有。“insert”和“contains”的复杂度都是“O(1)”。`O(1) + O(1) = O(1)`。然后,这两个操作将执行“n”次(每次调用传递给“filter”的闭包一次,每个元素调用一次)即,如果无论输入大小如何,操作都需要恒定的时间,那么仍然执行两次使其花费恒定的时间,而与输入大小无关。其总复杂度为“O(n)”。 (2认同)

Cœu*_*œur 7

https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift的启发,我们可以声明一个更强大的工具,该工具能够对任何keyPath进行唯一性过滤。感谢Alexander对复杂性的各种回答,以下解决方案应该是最佳的。

非变异解决方案

我们扩展了一个功能,该功能能够过滤任何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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您的对象不符合RangeReplaceableCollection,但符合Sequence,则可以具有此附加扩展名,但返回类型始终为Array:

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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

如果我们希望元素本身具有唯一性,如问题所示,我们可以使用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] */
Run Code Online (Sandbox Code Playgroud)

如果我们希望其他事物(例如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}] */
Run Code Online (Sandbox Code Playgroud)

变异溶液

我们扩展了一个可变功能,该功能能够过滤任何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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

如果我们希望元素本身具有唯一性,如问题所示,我们可以使用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] */
Run Code Online (Sandbox Code Playgroud)

如果我们希望其他事物(例如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}] */
Run Code Online (Sandbox Code Playgroud)


MH1*_*175 7

正如 WWDC 2021 上所指出的,Swift 拥有社区开发的算法、集合和数值包。算法包提供了一种uniqued()算法。

这些还不是 Swift 标准库的一部分。目前,您可以从 Apple 的 Github 页面下载它们和/或通过 Swift Package Manager 安装它们。

全球开发者大会视频:

https://developer.apple.com/videos/play/wwdc2021/10256/

GitHub页面:

https://github.com/apple/swift-algorithms

uniqued()uniqued(on:)文档:

https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md


Ala*_* T. 6

对于元素既不可散列也不可比较的数组(例如,复杂的对象,字典或结构),此扩展提供了一种通用方法来删除重复项:

extension Array
{
   func filterDuplicate<T:Hashable>(_ keyValue:(Element)->T) -> [Element]
   {
      var uniqueKeys = Set<T>()
      return filter{uniqueKeys.insert(keyValue($0)).inserted}
   }

   func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
   { 
      return filterDuplicate{"\(keyValue($0))"}
   }
}

// example usage: (for a unique combination of attributes):

peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }

or...

peopleArray = peopleArray.filterDuplicate{ "\(($0.name, $0.age, $0.sex))" }
Run Code Online (Sandbox Code Playgroud)

您不必费心将值设为Hashable,它使您可以使用不同的字段组合来实现唯一性。

注意:有关更健壮的方法,请参阅以下评论中Coeur提出的解决方案。

stackoverflow.com/a/55684308/1033581

[编辑] Swift 4替代

使用Swift 4.2,您可以使用Hasher类轻松构建哈希。可以更改上述扩展名以利用此功能:

extension Array
{
    func filterDuplicate(_ keyValue:((AnyHashable...)->AnyHashable,Element)->AnyHashable) -> [Element]
    {
        func makeHash(_ params:AnyHashable ...) -> AnyHashable
        { 
           var hash = Hasher()
           params.forEach{ hash.combine($0) }
           return hash.finalize()
        }  
        var uniqueKeys = Set<AnyHashable>()
        return filter{uniqueKeys.insert(keyValue(makeHash,$0)).inserted}     
    }
}
Run Code Online (Sandbox Code Playgroud)

调用语法略有不同,因为闭包收到了一个附加参数,该参数包含一个函数,用于对可变数量的值进行哈希处理(必须分别可哈希)

peopleArray = peopleArray.filterDuplicate{ $0($1.name, $1.age, $1.sex) } 
Run Code Online (Sandbox Code Playgroud)

它还将使用单个唯一性值(使用$ 1并忽略$ 0)。

peopleArray = peopleArray.filterDuplicate{ $1.name } 
Run Code Online (Sandbox Code Playgroud)

  • @AlainT。真的不要这样做。String的目的不是成为某些贫民窟临时密钥生成机制。只是将T限制为可散列。 (2认同)

小智 5

您可以直接使用set集合删除重复项,然后将其强制转换为数组

var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var mySet = Set<Int>(myArray)

myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]
Run Code Online (Sandbox Code Playgroud)

然后您可以根据需要订购阵列

myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
Run Code Online (Sandbox Code Playgroud)


Mah*_*ura 5

  1. 首先将数组的所有元素添加到 NSOrderedSet 中。
  2. 这将删除数组中的所有重复项。
  3. 再次将此有序集转换为数组。

完毕....

例子

let array = [1,1,1,1,2,2,2,2,4,6,8]

let orderedSet : NSOrderedSet = NSOrderedSet(array: array)

let arrayWithoutDuplicates : NSArray = orderedSet.array as NSArray
Run Code Online (Sandbox Code Playgroud)

arrayWithoutDuplicates 的输出 - [1,2,4,6,8]