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

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

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

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

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


当前回答

使用Set或NSOrderedSet删除重复项,然后转换回数组:

let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))

其他回答

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

(使用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]

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

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

您不必为使值可哈希而烦恼,它允许您使用不同的字段组合来实现惟一性。

注:对于更健壮的方法,请参阅下面评论中Coeur提出的解决方案。

stackoverflow.com/a/55684308/1033581

Swift 4的替代方案

在Swift 4.2中,你可以更容易地使用hash类来构建散列。上面的扩展可以改变,以利用这一点:

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

调用语法略有不同,因为闭包接收了一个额外的参数,其中包含一个函数,用于散列可变数量的值(这些值必须是单独可散列的)

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

它也可以使用单一唯一性值(使用$1而忽略$0)。

peopleArray = peopleArray.filterDuplicate{ $1.name } 

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

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中。

斯威夫特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]