我可能有一个像下面这样的数组:

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

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

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

请注意,删除了2、6和15的重复项,以确保每个相同的元素中只有一个。Swift是否提供了一种容易做到这一点的方法,还是我必须自己做?


当前回答

最简单的方法是使用NSOrderedSet,它存储唯一的元素并保持元素的顺序。如:

func removeDuplicates(from items: [Int]) -> [Int] {
    let uniqueItems = NSOrderedSet(array: items)
    return (uniqueItems.array as? [Int]) ?? []
}

let arr = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
removeDuplicates(from: arr)

其他回答

编辑/更新Swift 4或更高版本

我们还可以扩展RangeReplaceableCollection协议,以允许它也用于StringProtocol类型:

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 }
    }
}

let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]

"abcdefabcghi".orderedSet  // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"

变异的方法:

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

var substring = "abcdefabcdefghi".dropFirst(3)  // "defabcdefghi"
substring.removeDuplicates()
substring   // "defabcghi"

对于Swift 3,请点击这里

斯威夫特4

保证继续订购。

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

在数组中保留唯一值和排序。

(使用Swift 3)

    var top3score: [Int] = []


    outerLoop: for i in 0..<top10score.count {
        dlog(message: String(top10score[i]))

        if top3score.count == 3 {
            break
        }

        for aTop3score in top3score {
            if aTop3score == top10score[i] {
                continue outerLoop
            }
        }

        top3score.append(top10score[i])

    }

    print("top10score is \(top10score)")  //[14, 5, 5, 5, 3, 3, 2, 2, 2, 2]
    print("top3score is \(top3score)")   //[14, 5, 3]

斯威夫特5.7

使用有序集

您可以将具有重复元素的数组传递给以下通用函数,该函数处理有序Set并返回没有重复元素的新数组。

import Foundation

internal func withoutDuplicates<T>(_ array: [T]) -> [T] {
    
    let orderedSet: NSMutableOrderedSet = []
    var modifiedArray = [T]()
    
    orderedSet.addObjects(from: array)
    
    for i in 0...(orderedSet.count - 1) {
        modifiedArray.append(orderedSet[i] as! T)
    }
    return modifiedArray
}

////////////////////////////////////////////////////////////

let arrayOfStrings: [String] = ["A","A","A","B","B","C","C"]
let arrayOfIntegers: [UInt8] = [1, 1, 1, 2, 2, 2, 3, 3]
let arrayOfBooleans: [Bool] = [true, false, false, true]

let ordered_01 = withoutDuplicates(arrayOfStrings)
let ordered_02 = withoutDuplicates(arrayOfIntegers)
let ordered_03 = withoutDuplicates(arrayOfBooleans)

结果:

// ordered_01  –––>  ["A","B","C"]
// ordered_02  –––>  [1, 2, 3]
// ordered_03  –––>  [true, false]

使用无序集

如果新数组中元素的顺序对您来说并不重要,则在处理时使用无序集。无序集中的元素类型必须符合哈希协议。

import UIKit

fileprivate func noDuplicates<T: Hashable>(_ array: [T]) -> [T] {
    
    var unorderedSet = Set<T>()
    var modifiedArray: [T] = []
    
    for i in 0...(array.count - 1) {
        unorderedSet.insert(array[i])
    }
    for i in unorderedSet.indices {
        modifiedArray.append(unorderedSet[i])
    }
    return modifiedArray
}

////////////////////////////////////////////////////////////

let arrayOfInts: [Int] = [10, 5, 7, 200, -500, 10, 7, 5]
let arrayOfStrs: [String] = ["A","A","A","B","B","C","C"]
let arrayOfBools: [Bool] = [true, false, false, true]

let unordered_01 = noDuplicates(arrayOfInts)
let unordered_02 = noDuplicates(arrayOfStrs)
let unordered_03 = noDuplicates(arrayOfBools)

结果:

// unordered_01  –––>  [200, 7, 10, -500, 5]
// unordered_02  –––>  ["B", "C", "A"]
// unordered_03  –––>  [false, true]

下面是SequenceType上的一个类别,它保留了数组的原始顺序,但使用Set来进行contains查找,以避免数组的contains(_:)方法上的O(n)代价。

public extension Sequence where Element: Hashable {

    /// Return the sequence 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.
    func uniqued() -> [Element] {
        var seen = Set<Element>()
        return self.filter { seen.insert($0).inserted }
    }
}

如果你不是Hashable或Equatable,你可以传入一个谓词来进行相等性检查:

extension Sequence {

    /// Return the sequence with all duplicates removed.
    ///
    /// Duplicate, in this case, is defined as returning `true` from `comparator`.
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    func uniqued(comparator: @escaping (Element, Element) throws -> Bool) rethrows -> [Element] {
        var buffer: [Element] = []

        for element in self {
            // If element is already in buffer, skip to the next element
            if try buffer.contains(where: { try comparator(element, $0) }) {
                continue
            }

            buffer.append(element)
        }

        return buffer
    }
}

现在,如果你没有Hashable,但是是Equatable,你可以使用这个方法:

extension Sequence where Element: Equatable {

    /// Return the sequence with all duplicates removed.
    ///
    /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    func uniqued() -> [Element] {
        return self.uniqued(comparator: ==)
    }
}

最后,你可以添加一个unique的关键路径版本,如下所示:

extension Sequence {

    /// Returns the sequence with duplicate elements removed, performing the comparison using the property at
    /// the supplied keypath.
    ///
    /// i.e.
    ///
    /// ```
    /// [
    ///   MyStruct(value: "Hello"),
    ///   MyStruct(value: "Hello"),
    ///   MyStruct(value: "World")
    ///  ].uniqued(\.value)
    /// ```
    /// would result in
    ///
    /// ```
    /// [
    ///   MyStruct(value: "Hello"),
    ///   MyStruct(value: "World")
    /// ]
    /// ```
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    ///
    func uniqued<T: Equatable>(_ keyPath: KeyPath<Element, T>) -> [Element] {
        self.uniqued { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
    }
}

你可以把这两个都放在你的应用程序中,Swift会根据你的序列的迭代器选择正确的一个。元素类型。


对于El Capitan,您可以扩展此方法以包括多个键盘,如下所示:

    /// Returns the sequence with duplicate elements removed, performing the comparison using the property at
    /// the supplied keypaths.
    ///
    /// i.e.
    ///
    /// ```
    /// [
    ///   MyStruct(value1: "Hello", value2: "Paula"),
    ///   MyStruct(value1: "Hello", value2: "Paula"),
    ///   MyStruct(value1: "Hello", value2: "Bean"),
    ///   MyStruct(value1: "World", value2: "Sigh")
    ///  ].uniqued(\.value1, \.value2)
    /// ```
    /// would result in
    ///
    /// ```
    /// [
    ///   MyStruct(value1: "Hello", value2: "Paula"),
    ///   MyStruct(value1: "Hello", value2: "Bean"),
    ///   MyStruct(value1: "World", value2: "Sigh")
    /// ]
    /// ```
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    ///
    func uniqued<T: Equatable, U: Equatable>(_ keyPath1: KeyPath<Element, T>, _ keyPath2: KeyPath<Element, U>) -> [Element] {
        self.uniqued {
            $0[keyPath: keyPath1] == $1[keyPath: keyPath1] && $0[keyPath: keyPath2] == $1[keyPath: keyPath2]
        }
    }

但是(恕我直言)你最好把你自己的block传递给self.unique。