如何在Swift中创build唯一对象列表数组
我们如何用Objective-C中的NSSet
& NSMutableSet
等Swift语言创build独特的对象列表。
从Swift 1.2(Xcode 6.3 beta)开始,Swift有一个本地集合types。 从发行说明:
包含一个新的
Set
数据结构,它提供了具有完整值语义的唯一元素的通用集合。 它与NSSet
桥接,提供类似于Array
和Dictionary
。
这里有一些简单的用法示例:
// 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了一个类似于内置Array
和Dictionary
的广泛的Set
types – 这里是一个和两个博客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 }
要使用它,只需将包含重复元素的数组传递给此函数。 然后它将返回一个唯一性保证的数组。
如果你喜欢,你也可以传递一个Dictionary
, String
或任何符合ExtensibleCollectionType
协议的东西。