如何在Swift中创build唯一对象列表数组

我们如何用Objective-C中的NSSetNSMutableSet等Swift语言创build独特的对象列表。

从Swift 1.2(Xcode 6.3 beta)开始,Swift有一个本地集合types。 从发行说明:

包含一个新的Set数据结构,它提供了具有完整值语义的唯一元素的通用集合。 它与NSSet桥接,提供类似于ArrayDictionary

这里有一些简单的用法示例:

 // Create set from array literal: var set = Set([1, 2, 3, 2, 1]) // Add single elements: set.insert(4) set.insert(3) // Add multiple elements: set.unionInPlace([ 4, 5, 6 ]) // Swift 3: set.formUnion([ 4, 5, 6 ]) // Remove single element: set.remove(2) // Remove multiple elements: set.subtractInPlace([ 6, 7 ]) // Swift 3: set.subtract([ 6, 7 ]) print(set) // [5, 3, 1, 4] // Test membership: if set.contains(5) { print("yes") } 

但有更多的方法可用。

更新:集合现在也logging在Swift文档的“集合types”一章中。

您可以在Swift中使用任何Objective-C类:

 var set = NSMutableSet() set.addObject(foo) 

Swift没有套的概念。 在Swift中使用NSMutableSet可能比使用保存虚拟值的Dictionary慢。 你可以这样做:

 var mySet: Dictionary<String, Boolean> = [:] mySet["something"]= 1 

然后只是遍历键。

我已经构build了一个类似于内置ArrayDictionary的广泛的Settypes – 这里是一个和两个博客post和一个GitHub存储库:

  • 在Swift中创build一个集合types
  • 设置types后续
  • SwiftSets上GitHub
 extension Array where Element: Hashable { var setValue: Set<Element> { return Set<Element>(self) } } let numbers = [1,2,3,4,5,6,7,8,9,0,0,9,8,7] let uniqueNumbers = numbers.setValue // {0, 2, 4, 9, 5, 6, 7, 3, 1, 8} let names = ["John","Mary","Steve","Mary"] let uniqueNames = names.setValue // {"John", "Mary", "Steve"} 

我以为一个内部字典的结构将是要走的路。 我只是刚刚开始使用它,所以它不完整,我不知道性能。

 struct Set<T : Hashable> { var _items : Dictionary<T, Bool> = [:] mutating func add(newItem : T) { _items[newItem] = true } mutating func remove(newItem : T) { _items[newItem] = nil } func contains(item: T) -> Bool { if _items.indexForKey(item) != nil { return true } else { return false } } var items : [T] { get { return [T](_items.keys) } } var count : Int { get { return _items.count } } } 

你实际上可以创build一个Set对象很容易(与GoZoner相反,有一个内置的包含方法):

 class Set<T : Equatable> { var items : T[] = [] func add(item : T) { if !contains(items, {$0 == item}) { items += item } } } 

你甚至可能想要声明一个自定义运算符:

 @assignment @infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> { for item in items { set.add(item) } return set } 

总是在这种情况下,关键的因素是如何比较对象和什么types的对象进入设置。 使用Swift Dictionary(Set对象是字典键)可能是基于对键types(String,Int,Double,Bool,无价值枚举或可哈希)的限制的问题。

如果你可以在你的对象types上定义一个哈希函数 ,那么你可以使用一个字典。 如果对象是可订购的 ,那么你可以定义一个树。 如果对象只能与==相比,那么你需要遍历set元素来检测预先存在的对象。

 // When T is only Equatable class Set<T: Equatable> { var items = Array<T>() func hasItem (that: T) { // No builtin Array method of hasItem... // because comparison is undefined in builtin Array for this: T in items { if (this == that) { return true } } return false } func insert (that: T) { if (!hasItem (that)) items.append (that) } } 

以上是构buildSwift Set一个例子, 这个例子使用的是只有Equatable对象 – 尽pipe常见的情况并不一定会导致一个有效的Set实现(O(N)search的复杂性 – 上面的例子)。

所以我认为用数组创build一个Set是一个可怕的想法 – O(n)是这个集合的时间复杂度。

我已经把一个很好的集合,使用一个字典: https : //github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift

我写了一个函数来解决这个问题。

 public func removeDuplicates<C: ExtensibleCollectionType where C.Generator.Element : Equatable>(aCollection: C) -> C { var container = C() for element in aCollection { if !contains(container, element) { container.append(element) } } return container } 

要使用它,只需将包含重复元素的数组传递给此函数。 然后它将返回一个唯一性保证的数组。

如果你喜欢,你也可以传递一个DictionaryString或任何符合ExtensibleCollectionType协议的东西。