Removing duplicate elements from an array in Swift

后端 未结 30 2051
遥遥无期
遥遥无期 2020-11-22 00:07

I might have an array that looks like the following:

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

Or, reall

30条回答
  •  悲&欢浪女
    2020-11-22 00:59

    Here's a category on SequenceType which preserves the original order of the array, but uses a Set to do the contains lookups to avoid the O(n) cost on Array's contains(_:) method.

    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()
            return self.filter { seen.insert($0).inserted }
        }
    }
    

    If you aren't Hashable or Equatable, you can pass in a predicate to do the equality check:

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

    Now, if you don't have Hashable, but are Equatable, you can use this method:

    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: ==)
        }
    }
    

    Finally, you can add a key path version of uniqued like this:

    extension Sequence {
    
        /// Returns the sequence with duplicate elements removed, performing the comparison usinig 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(_ keyPath: KeyPath) -> [Element] {
            self.uniqued { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
        }
    }
    

    You can stick both of these into your app, Swift will choose the right one depending on your sequence's Iterator.Element type.

提交回复
热议问题