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

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

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

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

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


当前回答

让我提出一个类似于斯科特·加德纳的答案,但使用了更简洁的reduce语法。 此解决方案从自定义对象数组中删除重复项(保持初始顺序)

// Custom Struct. Can be also class. 
// Need to be `equitable` in order to use `contains` method below
struct CustomStruct : Equatable {
      let name: String
      let lastName : String
    }

// conform to Equatable protocol. feel free to change the logic of "equality"
func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
  return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
}

let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
                  CustomStruct(name: "name2", lastName: "lastName1"),
                  CustomStruct(name: "name1", lastName: "lastName1")]
print(categories.count) // prints 3

// remove duplicates (and keep initial order of elements)
let uniq1 : [CustomStruct] = categories.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
print(uniq1.count) // prints 2 - third element has removed

如果你想知道这个约简魔法是如何工作的,这里是完全相同的,只是使用了更扩展的约简语法

let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
  var newResult = result
  if (newResult.contains(category)) {}
  else {
    newResult.append(category)
  }
  return newResult
}
uniq2.count // prints 2 - third element has removed

你可以简单地复制粘贴这段代码到Swift Playground中。

其他回答

你可以自己卷,比如这样:

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]

让我提出一个类似于斯科特·加德纳的答案,但使用了更简洁的reduce语法。 此解决方案从自定义对象数组中删除重复项(保持初始顺序)

// Custom Struct. Can be also class. 
// Need to be `equitable` in order to use `contains` method below
struct CustomStruct : Equatable {
      let name: String
      let lastName : String
    }

// conform to Equatable protocol. feel free to change the logic of "equality"
func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
  return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
}

let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
                  CustomStruct(name: "name2", lastName: "lastName1"),
                  CustomStruct(name: "name1", lastName: "lastName1")]
print(categories.count) // prints 3

// remove duplicates (and keep initial order of elements)
let uniq1 : [CustomStruct] = categories.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
print(uniq1.count) // prints 2 - third element has removed

如果你想知道这个约简魔法是如何工作的,这里是完全相同的,只是使用了更扩展的约简语法

let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
  var newResult = result
  if (newResult.contains(category)) {}
  else {
    newResult.append(category)
  }
  return newResult
}
uniq2.count // prints 2 - third element has removed

你可以简单地复制粘贴这段代码到Swift Playground中。

我的解决方案,它似乎可以在O(n)时间,因为哈希映射访问是O(1),过滤器是O(n)。它还使用by闭包来选择按顺序区分元素的属性。

extension Sequence {

    func distinct<T: Hashable>(by: (Element) -> T) -> [Element] {
        var seen: [T: Bool] = [:]
        return self.filter { seen.updateValue(true, forKey: by($0)) == nil }
    }
}

我使用了@Jean-Philippe Pellet的答案,并做了一个数组扩展,对数组进行类似set的操作,同时保持元素的顺序。

/// Extensions for performing set-like operations on lists, maintaining order
extension Array where Element: Hashable {
  func unique() -> [Element] {
    var seen: [Element:Bool] = [:]
    return self.filter({ seen.updateValue(true, forKey: $0) == nil })
  }

  func subtract(takeAway: [Element]) -> [Element] {
    let set = Set(takeAway)
    return self.filter({ !set.contains($0) })
  }

  func intersect(with: [Element]) -> [Element] {
    let set = Set(with)
    return self.filter({ set.contains($0) })
  }
}

你可以很容易地转换为一个Set和一个数组:

let unique = Array(Set(originals))

这并不能保证保持数组的原始顺序。