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

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

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

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

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


当前回答

如果你把两个扩展都放在你的代码中,更快的Hashable版本将在可能的情况下使用,Equatable版本将用作备用版本。

public extension Sequence where Element: Hashable {
  /// The elements of the sequence, with duplicates removed.
  /// - Note: Has equivalent elements to `Set(self)`.
  @available(
  swift, deprecated: 5.4,
  message: "Doesn't compile without the constant in Swift 5.3."
  )
  var firstUniqueElements: [Element] {
    let getSelf: (Element) -> Element = \.self
    return firstUniqueElements(getSelf)
  }
}

public extension Sequence where Element: Equatable {
  /// The elements of the sequence, with duplicates removed.
  /// - Note: Has equivalent elements to `Set(self)`.
  @available(
  swift, deprecated: 5.4,
  message: "Doesn't compile without the constant in Swift 5.3."
  )
  var firstUniqueElements: [Element] {
    let getSelf: (Element) -> Element = \.self
    return firstUniqueElements(getSelf)
  }
}

public extension Sequence {
  /// The elements of the sequences, with "duplicates" removed
  /// based on a closure.
  func firstUniqueElements<Hashable: Swift.Hashable>(
    _ getHashable: (Element) -> Hashable
  ) -> [Element] {
    var set: Set<Hashable> = []
    return filter { set.insert(getHashable($0)).inserted }
  }

  /// The elements of the sequence, with "duplicates" removed,
  /// based on a closure.
  func firstUniqueElements<Equatable: Swift.Equatable>(
    _ getEquatable: (Element) -> Equatable
  ) -> [Element] {
    reduce(into: []) { uniqueElements, element in
      if zip(
        uniqueElements.lazy.map(getEquatable),
        AnyIterator { [equatable = getEquatable(element)] in equatable }
      ).allSatisfy(!=) {
        uniqueElements.append(element)
      }
    }
  }
}

如果顺序不重要,那么你总是可以使用这个Set初始化式。

其他回答

我创建了一个时间复杂度为o(n)的高阶函数。另外,像map这样的功能可以返回您想要的任何类型。

extension Sequence {
    func distinct<T,U>(_ provider: (Element) -> (U, T)) -> [T] where U: Hashable {
        var uniqueKeys = Set<U>()
        var distintValues = [T]()
        for object in self {
            let transformed = provider(object)
            if !uniqueKeys.contains(transformed.0) {
                distintValues.append(transformed.1)
                uniqueKeys.insert(transformed.0)
            }
        }
        return distintValues
    }
}

正如WWDC 2021所指出的那样,Swift有社区开发的算法、集合和数字包。算法包有一个唯一的()算法。

这些还不是Swift标准库的一部分。你目前可以从苹果的Github页面下载它们和/或通过Swift软件包管理器安装它们。

WWDC视频:

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

Github页面:

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

unique()和unique (on:)文档:

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

这里我对对象做了O(n)解。不是少行解决方案,而是……

struct DistinctWrapper <T>: Hashable {
    var underlyingObject: T
    var distinctAttribute: String
    var hashValue: Int {
        return distinctAttribute.hashValue
    }
}
func distinct<S : SequenceType, T where S.Generator.Element == T>(source: S,
                                                                distinctAttribute: (T) -> String,
                                                                resolution: (T, T) -> T) -> [T] {
    let wrappers: [DistinctWrapper<T>] = source.map({
        return DistinctWrapper(underlyingObject: $0, distinctAttribute: distinctAttribute($0))
    })
    var added = Set<DistinctWrapper<T>>()
    for wrapper in wrappers {
        if let indexOfExisting = added.indexOf(wrapper) {
            let old = added[indexOfExisting]
            let winner = resolution(old.underlyingObject, wrapper.underlyingObject)
            added.insert(DistinctWrapper(underlyingObject: winner, distinctAttribute: distinctAttribute(winner)))
        } else {
            added.insert(wrapper)
        }
    }
    return Array(added).map( { return $0.underlyingObject } )
}
func == <T>(lhs: DistinctWrapper<T>, rhs: DistinctWrapper<T>) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

// tests
// case : perhaps we want to get distinct addressbook list which may contain duplicated contacts like Irma and Irma Burgess with same phone numbers
// solution : definitely we want to exclude Irma and keep Irma Burgess
class Person {
    var name: String
    var phoneNumber: String
    init(_ name: String, _ phoneNumber: String) {
        self.name = name
        self.phoneNumber = phoneNumber
    }
}

let persons: [Person] = [Person("Irma Burgess", "11-22-33"), Person("Lester Davidson", "44-66-22"), Person("Irma", "11-22-33")]
let distinctPersons = distinct(persons,
    distinctAttribute: { (person: Person) -> String in
        return person.phoneNumber
    },
    resolution:
    { (p1, p2) -> Person in
        return p1.name.characters.count > p2.name.characters.count ? p1 : p2
    }
)
// distinctPersons contains ("Irma Burgess", "11-22-33") and ("Lester Davidson", "44-66-22")

如果你把两个扩展都放在你的代码中,更快的Hashable版本将在可能的情况下使用,Equatable版本将用作备用版本。

public extension Sequence where Element: Hashable {
  /// The elements of the sequence, with duplicates removed.
  /// - Note: Has equivalent elements to `Set(self)`.
  @available(
  swift, deprecated: 5.4,
  message: "Doesn't compile without the constant in Swift 5.3."
  )
  var firstUniqueElements: [Element] {
    let getSelf: (Element) -> Element = \.self
    return firstUniqueElements(getSelf)
  }
}

public extension Sequence where Element: Equatable {
  /// The elements of the sequence, with duplicates removed.
  /// - Note: Has equivalent elements to `Set(self)`.
  @available(
  swift, deprecated: 5.4,
  message: "Doesn't compile without the constant in Swift 5.3."
  )
  var firstUniqueElements: [Element] {
    let getSelf: (Element) -> Element = \.self
    return firstUniqueElements(getSelf)
  }
}

public extension Sequence {
  /// The elements of the sequences, with "duplicates" removed
  /// based on a closure.
  func firstUniqueElements<Hashable: Swift.Hashable>(
    _ getHashable: (Element) -> Hashable
  ) -> [Element] {
    var set: Set<Hashable> = []
    return filter { set.insert(getHashable($0)).inserted }
  }

  /// The elements of the sequence, with "duplicates" removed,
  /// based on a closure.
  func firstUniqueElements<Equatable: Swift.Equatable>(
    _ getEquatable: (Element) -> Equatable
  ) -> [Element] {
    reduce(into: []) { uniqueElements, element in
      if zip(
        uniqueElements.lazy.map(getEquatable),
        AnyIterator { [equatable = getEquatable(element)] in equatable }
      ).allSatisfy(!=) {
        uniqueElements.append(element)
      }
    }
  }
}

如果顺序不重要,那么你总是可以使用这个Set初始化式。

我相信提供一个uniq()和uniqInPlace()函数通过删除数组的值来改变数组是很好的。这类似于Swift提供的sort()和sortInPlace()函数。此外,由于它是一个数组,它应该保持它的原始元素顺序。

extension Array where Element: Equatable {

    public func uniq() -> [Element] {
        var arrayCopy = self
        arrayCopy.uniqInPlace()
        return arrayCopy
    }

    mutating public func uniqInPlace() {
        var seen = [Element]()
        var index = 0
        for element in self {
            if seen.contains(element) {
                removeAtIndex(index)
            } else {
                seen.append(element)
                index++
            }
        }
    }
}

你只能在变量数组(即var)上使用uniqInPlace(),因为你不能改变常量数组(即let)。

一些用法示例:

var numbers = [1, 6, 2, 2, 4, 1, 5]
numbers.uniqInPlace() // array is now [1, 6, 2, 4, 5]

let strings = ["Y", "Z", "A", "Y", "B", "Y", "Z"]
let uniqStrings = strings.uniq() // uniqStrings is now ["Y", "Z", "A", "B"]