在Swift中删除数组中的重复元素

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

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

Or, really, any sequence of like-typed portions of data. What I want to do is ensure that there is only one of each identical element. For example, the above array would become:

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

Notice that the duplicates of 2, 6, and 15 were removed to ensure that there was only one of each identical element. Does Swift provide a way to do this easily, or will I have to do it myself?

270416 次浏览

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

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]

在这里中使用不可变类型而不是变量的替代(如果不是最优)解决方案:

func deleteDuplicates<S: ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
let s = reduce(seq, S()){
ac, x in contains(ac,x) ? ac : ac + [x]
}
return s
}

包括对比Jean-Pillippe的命令式方法和函数式方法。

作为奖励,这个函数不仅可以处理数组,还可以处理字符串!

编辑:这个答案是在2014年为Swift 1.0编写的(在Set在Swift中可用之前)。它不需要Hashable一致性&在二次时间内运行。

您总是可以使用Dictionary,因为Dictionary只能保存惟一的值。例如:

var arrayOfDates: NSArray = ["15/04/01","15/04/01","15/04/02","15/04/02","15/04/03","15/04/03","15/04/03"]


var datesOnlyDict = NSMutableDictionary()
var x = Int()


for (x=0;x<(arrayOfDates.count);x++) {
let date = arrayOfDates[x] as String
datesOnlyDict.setValue("foo", forKey: date)
}


let uniqueDatesArray: NSArray = datesOnlyDict.allKeys // uniqueDatesArray = ["15/04/01", "15/04/03", "15/04/02"]


println(uniqueDatesArray.count)  // = 3

正如你所看到的,生成的数组并不总是按“顺序”排列。如果你想对数组排序,添加这个:

var sortedArray = sorted(datesOnlyArray) {
(obj1, obj2) in


let p1 = obj1 as String
let p2 = obj2 as String
return p1 < p2
}


println(sortedArray) // = ["15/04/01", "15/04/02", "15/04/03"]

你可以很容易地转换为Set并再次转换回Array:

let unique = Array(Set(originals))

这是,保证保持数组的原始顺序。

您可以直接使用set集合删除重复,然后将其转换回数组

var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var mySet = Set<Int>(myArray)


myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]

然后你可以按你想要的顺序排列你的数组

myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]

这里我对对象做了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")

斯威夫特2

使用< em > uniq < / em >函数

func uniq<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter({ (v) -> Bool in
return seen.updateValue(true, forKey: v) == nil
})
}

使用:

var test = [1,2,3,4,5,6,7,8,9,9,9,9,9,9]
print(uniq(test)) //1,2,3,4,5,6,7,8,9

如果你把两个扩展都放在你的代码中,更快的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初始值设定项

我使用了@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) })
}
}

稍微简洁一点的丹尼尔·克罗姆的快速回答语法版本,使用了一个尾随闭包和简写参数名,这似乎是基于空速速度的原始答案:

func uniq<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {
var seen = [E: Bool]()
return source.filter { seen.updateValue(true, forKey: $0) == nil }
}

实现可与uniq(_:)一起使用的自定义类型的示例(必须符合Hashable,因此符合Equatable,因为Hashable扩展了Equatable):

func ==(lhs: SomeCustomType, rhs: SomeCustomType) -> Bool {
return lhs.id == rhs.id // && lhs.someOtherEquatableProperty == rhs.someOtherEquatableProperty
}


struct SomeCustomType {


let id: Int


// ...


}


extension SomeCustomType: Hashable {


var hashValue: Int {
return id
}


}

在上面的代码中…

==重载中使用的id可以是任何Equatable类型(或返回Equatable类型的方法,例如someMethodThatReturnsAnEquatableType())。注释掉的代码演示了扩展相等性检查,其中someOtherEquatablePropertyEquatable类型的另一个属性(但也可以是返回Equatable类型的方法)。

id,如在hashValue computed属性中使用(必须符合Hashable),可以是任何Hashable(因此是Equatable)属性(或返回Hashable类型的方法)。

使用uniq(_:)的例子:

var someCustomTypes = [SomeCustomType(id: 1), SomeCustomType(id: 2), SomeCustomType(id: 3), SomeCustomType(id: 1)]


print(someCustomTypes.count) // 4


someCustomTypes = uniq(someCustomTypes)


print(someCustomTypes.count) // 3

我相信提供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"]

编辑/更新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"

对于斯威夫特3,单击在这里

这里有很多答案,但我错过了这个简单的扩展,适合Swift 2及以上:

extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()


for value in self {
if result.contains(value) == false {
result.append(value)
}
}


return result
}
}

这非常简单。可以这样调用:

let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]

基于属性的过滤

要根据属性筛选数组,你可以使用这个方法:

extension Array {


func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
var results = [Element]()


forEach { (element) in
let existingElements = results.filter {
return includeElement(lhs: element, rhs: $0)
}
if existingElements.count == 0 {
results.append(element)
}
}


return results
}
}

你可以这样调用它:

let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }

下面是SequenceType上的一个类别,它保留数组的原始顺序,但使用Set来进行contains查找,以避免array的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会根据你的序列的Iterator.Element类型选择正确的一个。


对于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]
}
}

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

这只是一个非常简单和方便的实现。具有相等元素的数组扩展中的计算属性。

extension Array where Element: Equatable {
/// Array containing only _unique_ elements.
var unique: [Element] {
var result: [Element] = []
for element in self {
if !result.contains(element) {
result.append(element)
}
}


return result
}
}
让我建议一个类似斯科特·加德纳的回答的答案,但使用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中。

在Swift 3.0中,我发现了最简单和最快的解决方案,可以在保持顺序的同时消除重复的元素:

extension Array where Element:Hashable {
var unique: [Element] {
var set = Set<Element>() //the unique list kept in a Set for fast retrieval
var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
for value in self {
if !set.contains(value) {
set.insert(value)
arrayOrdered.append(value)
}
}


return arrayOrdered
}
}
func removeDublicate (ab: [Int]) -> [Int] {
var answer1:[Int] = []
for i in ab {
if !answer1.contains(i) {
answer1.append(i)
}}
return answer1
}

用法:

let f = removeDublicate(ab: [1,2,2])
print(f)

从数组中删除重复项的另一个Swift 3.0解决方案。该解决方案改进了许多已经提出的其他解决方案:

  • 保留输入数组中元素的顺序
  • 线性复杂度O(n):单通滤波器O(n) +集插入O(1)

给定整数数组:

let numberArray = [10, 1, 2, 3, 2, 1, 15, 4, 5, 6, 7, 3, 2, 12, 2, 5, 5, 6, 10, 7, 8, 3, 3, 45, 5, 15, 6, 7, 8, 7]

功能代码:

func orderedSet<T: Hashable>(array: Array<T>) -> Array<T> {
var unique = Set<T>()
return array.filter { element in
return unique.insert(element).inserted
}
}


orderedSet(array: numberArray)  // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]

数组扩展代码:

extension Array where Element:Hashable {
var orderedSet: Array {
var unique = Set<Element>()
return filter { element in
return unique.insert(element).inserted
}
}
}


numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]

这段代码利用了Set上的insert操作返回的结果,该操作在O(1)上执行,并返回一个元组,指示该项是否被插入或是否已经存在于集合中。

如果项在集合中,filter将从最终结果中排除它。

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

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 }

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

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

使用SetNSOrderedSet删除重复项,然后转换回Array:

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

斯威夫特4

public extension Array where Element: Hashable {
func uniqued() -> [Element] {
var seen = Set<Element>()
return filter{ seen.insert($0).inserted }
}
}

每次尝试insert也将返回一个元组:(inserted: Bool, memberAfterInsert: Set.Element)。看到文档

使用返回值意味着我们可以避免进行多个循环,因此这是O(n)。

为此,我做了一个尽可能简单的扩展。

extension Array where Element: Equatable {


func containsHowMany(_ elem: Element) -> Int {
return reduce(0) { $1 == elem ? $0 + 1 : $0 }
}


func duplicatesRemoved() -> Array {
return self.filter { self.containsHowMany($0) == 1 }
}


mutating func removeDuplicates() {
self = self.duplicatesRemoved(()
}
}

你可以使用duplicatesRemoved()来获取一个新数组,它的重复元素被移除,或者使用removeDuplicates()来改变自己。看到的:

let arr = [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]


let noDuplicates = arr.duplicatesRemoved()
print(arr) // [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]
print(noDuplicates) // [1, 2, 3, 4, 5, 6, 7, 8]


arr.removeDuplicates()
print(arr) // [1, 2, 3, 4, 5, 6, 7, 8]

斯威夫特4

保证继续订购。

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

下面是使用自定义匹配函数使序列唯一的更灵活的方法。

extension Sequence where Iterator.Element: Hashable {


func unique(matching: (Iterator.Element, Iterator.Element) -> Bool) -> [Iterator.Element] {


var uniqueArray: [Iterator.Element] = []
forEach { element in
let isUnique = uniqueArray.reduce(true, { (result, item) -> Bool in
return result && matching(element, item)
})
if isUnique {
uniqueArray.append(element)
}
}
return uniqueArray
}
}

如果你需要值排序,这是工作(Swift 4)

let sortedValues = Array(Set(array)).sorted()

最简单的方法是使用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中是有效的,如果你不想/需要将结果转换为数组,但可以使用Set。默认情况下,结果没有排序,但是可以使用sorted()这样做,它返回一个数组,如print语句所示。

let array = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]


var result = Set<Int>()
_ = array.map{ result.insert($0) }


print(result.sorted())  // [1, 2, 4, 6, 15, 24, 60]

斯威夫特4. x:

extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
return Array(Set<Iterator.Element>(self))
}


func uniqueOrdered() -> [Iterator.Element] {
return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
}
}

用法:

["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()

["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()

斯威夫特5

extension Sequence where Element: Hashable {
func unique() -> [Element] {
NSOrderedSet(array: self as! [Any]).array as! [Element]
}
}

这是swift 4.2中最简单的方法,如下所示的代码

let keyarray:NSMutableArray = NSMutableArray()


for  object in dataArr
{
if !keysArray.contains(object){
keysArray.add(object)
}
}


print(keysArray)

Xcode 10.1 - Swift 4.2简单而强大的解决方案

func removeDuplicates(_ nums: inout [Int]) -> Int {
nums = Set(nums).sorted()
return nums.count
}

例子

var arr = [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9]
removeDuplicates(&arr)


print(arr) // [1,2,3,4,5,6,7,8,9]

https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift的启发,我们可以声明一个更强大的工具,它能够过滤任意keyPath上的唯一性。感谢Alexander关于复杂性的各种答案的评论,下面的解决方案应该是最优的。

Non-mutating解决方案

我们扩展了一个函数,它可以过滤任意keyPath上的唯一性:

extension RangeReplaceableCollection {
/// Returns a collection containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> Self {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}

注意:在你的对象不符合RangeReplaceableCollection,但符合Sequence的情况下,你可以有这个额外的扩展,但返回类型将始终是一个数组:

extension Sequence {
/// Returns an array containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> [Element] {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}

使用

如果我们想要元素本身的唯一性,如问题中所示,我们使用keyPath \.self:

let a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let b = a.unique(for: \.self)
/* b is [1, 4, 2, 6, 24, 15, 60] */

如果我们想要其他东西的唯一性(比如对象集合的id),则使用我们选择的keyPath:

let a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
let b = a.unique(for: \.y)
/* b is [{x 1 y 1}, {x 1 y 2}] */

变异的解决方案

我们扩展了一个变异函数,它能够过滤任意keyPath上的唯一性:

extension RangeReplaceableCollection {
/// Keeps only, in order, the first instances of
/// elements of the collection that compare equally for the keyPath.
mutating func uniqueInPlace<T: Hashable>(for keyPath: KeyPath<Element, T>) {
var unique = Set<T>()
removeAll { !unique.insert($0[keyPath: keyPath]).inserted }
}
}

使用

如果我们想要元素本身的唯一性,如问题中所示,我们使用keyPath \.self:

var a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
a.uniqueInPlace(for: \.self)
/* a is [1, 4, 2, 6, 24, 15, 60] */

如果我们想要其他东西的唯一性(比如对象集合的id),则使用我们选择的keyPath:

var a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
a.uniqueInPlace(for: \.y)
/* a is [{x 1 y 1}, {x 1 y 2}] */

像函数式程序员一样思考:)

要根据元素是否已经出现来筛选列表,需要索引。可以使用enumerated获取索引,使用map返回值列表。

let unique = myArray
.enumerated()
.filter{ myArray.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }

这保证了秩序。如果你不介意顺序,那么Array(Set(myArray))的现有答案更简单,可能更有效。


更新:一些关于效率和正确性的注意事项

一些人对效率进行了评论。我肯定是先写正确和简单的代码,然后再找出瓶颈,尽管我知道这是否比Array(Set(array))更清楚是有争议的。

这个方法比Array(Set(array))慢得多。正如评论中所指出的,它确实保持了顺序,并对非Hashable的元素起作用。

然而,@Alain T的方法也保持了秩序,也快得多。所以除非你的元素类型是不可哈希的,或者你只是需要一个快速的一行,那么我建议采用他们的解决方案。

以下是MacBook Pro(2014)在Xcode 11.3.1 (Swift 5.1)发布模式下的一些测试。

profiler函数和两个比较方法:

func printTimeElapsed(title:String, operation:()->()) {
var totalTime = 0.0
for _ in (0..<1000) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
totalTime += timeElapsed
}
let meanTime = totalTime / 1000
print("Mean time for \(title): \(meanTime) s")
}


func method1<T: Hashable>(_ array: Array<T>) -> Array<T> {
return Array(Set(array))
}


func method2<T: Equatable>(_ array: Array<T>) -> Array<T>{
return array
.enumerated()
.filter{ array.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }
}


// Alain T.'s answer (adapted)
func method3<T: Hashable>(_ array: Array<T>) -> Array<T> {
var uniqueKeys = Set<T>()
return array.filter{uniqueKeys.insert($0).inserted}
}

以及少量的测试输入:

func randomString(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}


let shortIntList = (0..<100).map{_ in Int.random(in: 0..<100) }
let longIntList = (0..<10000).map{_ in Int.random(in: 0..<10000) }
let longIntListManyRepetitions = (0..<10000).map{_ in Int.random(in: 0..<100) }
let longStringList = (0..<10000).map{_ in randomString(1000)}
let longMegaStringList = (0..<10000).map{_ in randomString(10000)}

给出输出:

Mean time for method1 on shortIntList: 2.7358531951904296e-06 s
Mean time for method2 on shortIntList: 4.910230636596679e-06 s
Mean time for method3 on shortIntList: 6.417632102966309e-06 s
Mean time for method1 on longIntList: 0.0002518167495727539 s
Mean time for method2 on longIntList: 0.021718120217323302 s
Mean time for method3 on longIntList: 0.0005312927961349487 s
Mean time for method1 on longIntListManyRepetitions: 0.00014377200603485108 s
Mean time for method2 on longIntListManyRepetitions: 0.0007293639183044434 s
Mean time for method3 on longIntListManyRepetitions: 0.0001843773126602173 s
Mean time for method1 on longStringList: 0.007168249964714051 s
Mean time for method2 on longStringList: 0.9114790915250778 s
Mean time for method3 on longStringList: 0.015888616919517515 s
Mean time for method1 on longMegaStringList: 0.0525397013425827 s
Mean time for method2 on longMegaStringList: 1.111266262292862 s
Mean time for method3 on longMegaStringList: 0.11214958941936493 s

我的解决方案,它似乎可以在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 }
}
}

我创建了一个时间复杂度为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
}
}
  1. 首先将数组的所有元素添加到NSOrderedSet中。
  2. 这将删除数组中的所有重复项。
  3. 再次将这个orderedset转换为一个数组。

做……

例子

let array = [1,1,1,1,2,2,2,2,4,6,8]


let orderedSet : NSOrderedSet = NSOrderedSet(array: array)


let arrayWithoutDuplicates : NSArray = orderedSet.array as NSArray

输出arraywithoutduplates - [1,2,4,6,8]

在Swift 5中

 var array: [String] =  ["Aman", "Sumit", "Aman", "Sumit", "Mohan", "Mohan", "Amit"]


let uniq = Array(Set(array))
print(uniq)

输出将是

 ["Sumit", "Mohan", "Amit", "Aman"]

我认为这是更好的理解逻辑的方法

var arrayOfInts = [2, 2, 4, 4]
var mainArray = [Int]()


for value in arrayOfInts {


if mainArray.contains(value) != true  {
    

mainArray.append(value)
print("mainArray:\(mainArray)")
}}

Swift 3/ Swift 4/ Swift 5

只需要一行代码就可以省略数组副本而不影响顺序:

let filteredArr = Array(NSOrderedSet(array: yourArray))

这里有一个解决方案

  • 不使用遗留的NS类型
  • 是合理的快速O(n)
  • 简洁
  • 保持元素顺序
extension Array where Element: Hashable {


var uniqueValues: [Element] {
var allowed = Set(self)
return compactMap { allowed.remove($0) }
}
}

如果你想保持秩序,那么就用这个

let fruits = ["apple", "pear", "pear", "banana", "apple"]
let orderedNoDuplicates = Array(NSOrderedSet(array: fruits).map({ $0 as! String }))
var numbers = [1,2,3,4,5,10,10, 12, 12, 6,6,6,7,8,8, 8, 8, 8 , 7 , 1 , 1, 2 , 9]


var newArr : [Int] = []
for n in numbers {
if !newArr.contains(n) {
newArr.append(n)
}
}

输出- [1,2,3,4,5,10,12,6,7,8,9]

上面的解决方案保持顺序,但非常缓慢,因为.contains一遍又一遍地迭代。 因此使用有序集。

这将打印有序数组。

Array(NSOrderedSet.init(array: numbers))

输出- [1,2,3,4,5,10,12,6,7,8,9]

这将打印一个无序数组。

let uniqueUnordered = Array(Set(numbers))

输出- [4,2,1,9,10,3,5,6,8,12,7]

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

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

WWDC视频:

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

Github页面:

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

uniqued()uniqued(on:)文档:

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

包含相等性检查,而插入检查哈希,最安全的检查方式如下:

extension Array where Element: Hashable {


/// Big O(N) version. Updated since @Adrian's comment.
var uniques: Array {
// Go front to back, add element to buffer if it isn't a repeat.
var buffer: [Element] = []
var dictionary: [Element: Int] = [:]
for element in self where dictionary[element] == nil {
buffer.append(element)
dictionary[element] = 1
}
return buffer
}
}

现在不需要写扩展了。

Apple终于在其算法包中引入了uniqued()方法,可以在符合Sequence协议的任何类型上使用。

import Algorithms


let numbers = [1, 2, 3, 3, 2, 3, 3, 2, 2, 2, 1]
print(numbers.uniqued()) // prints [1, 2, 3]

更多信息https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md

从数组中删除重复项的简单方法

extension Array where Element: Equatable {
mutating func removeDuplicates() {
var result = [Element]()
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}}

< >强劲迅速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]

< br >

使用无序集

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

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]