【问题标题】:How to create array of unique object list in Swift如何在 Swift 中创建唯一对象列表数组
【发布时间】:2014-06-04 17:48:24
【问题描述】:

我们如何在 Swift 语言中创建唯一的对象列表,例如 Objective-C 中的 NSSetNSMutableSet

【问题讨论】:

  • 为什么会被否决? Swift 有 NSArray 等价物和 NSDictionary 等价物,也许它也有集合,但它们根本没有记录?
  • 人们对 swift 问题投了反对票,却没有考虑到这些问题可能尚未记录在案。
  • Swift 1.2 添加了Set 类型。
  • ……我想我也应该说 Swift 1.2 在 Xcode 6.3 beta 中可用。

标签: swift nsset


【解决方案1】:

从 Swift 1.2(Xcode 6.3 beta)开始,Swift 有一个原生的 set 类型。 来自发行说明:

包含一个新的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")
}

但还有更多可用的方法。

更新:集合现在也记录在 Swift 文档的 "Collection Types" 章节中。

【讨论】:

  • 是的,现在只需将NSMutableSetaddObjectremoveObjectallObject 一起使用即可。原生的Set 以后再用会是件好事。
【解决方案2】:

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

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

【讨论】:

  • 你可以,但是如果你尝试在其中放入一个 Swift 对象,它会抱怨它没有扩展 AnyObject
【解决方案3】:

Swift 没有集合的概念。在 Swift 中使用 NSMutableSet 可能比使用包含虚拟值的 Dictionary 慢。你可以这样做:

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

然后只需遍历键。

【讨论】:

  • Swift 中的布尔值与数字不同。您的意思可能是 true 而不是 1。
  • @KPM 您正在考虑 Bool 结构; Boolean 是 Swift 中 UInt8 的类型别名。
  • IMO 使用 Bool 而不是 Boolean。 Bool 是一种 swift 数据类型,取值 true / false,可以存储在字典中
【解决方案4】:

我已经构建了一个扩展的 Set 类型,类似于内置的 ArrayDictionary - 这里是博客文章一和二以及一个 GitHub 存储库:

【讨论】:

    【解决方案5】:
    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"}
    

    【讨论】:

      【解决方案6】:

      我认为带有内部字典的结构将是可行的方法。我才刚刚开始使用它,所以它还没有完成,我还不知道性能。

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

      【讨论】:

        【解决方案7】:

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

        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
        }
        

        【讨论】:

        • 我不知道为什么这被否决了,它不是很有效,但它绝对是小集合的可行解决方案。
        【解决方案8】:

        在这种情况下,关键因素始终是如何比较对象以及将哪些类型的对象放入集合中。使用 Swift 字典,其中 Set 对象是字典键,基于键类型(String、Int、Double、Bool、无值枚举或哈希)的限制可能会出现问题。

        如果您可以在对象类型上定义散列函数,那么您可以使用字典。 如果对象是可排序的,那么您可以定义一棵树。 如果对象只能与 == 进行比较,那么您需要遍历集合元素以检测预先存在的对象。

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

        上面是一个构建Swift的例子Set;该示例使用的对象仅为 Equatable - 虽然这是一种常见情况,但不一定会导致有效的 Set 实现(O(N) 搜索复杂度 - 以上是一个示例)。

        【讨论】:

        • 我认为 Equatable 会比 Comparable 更正确。 Equatable 定义为 protocol Equatable { func ==(lhs: Self, rhs: Self) -&gt; Bool } 而 Comparable 定义为 protocol Comparable : Equatable { func &lt;=(lhs: Self, rhs: Self) -&gt; Bool func &gt;=(lhs: Self, rhs: Self) -&gt; Bool func &gt;(lhs: Self, rhs: Self) -&gt; Bool }
        【解决方案9】:

        所以我认为用数组创建一个 Set 是一个糟糕的主意 - O(n) 是该集合的时间复杂度。

        我整理了一个使用字典的漂亮 Set:https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift

        【讨论】:

        • 虽然对于少数元素,数组可能要快得多。 Cocoa 集合类通常会根据您使用的元素数量选择返回 NSArray、NSSet 等的不同子类。
        • set 实现不考虑哈希冲突,即两个对象创建相同的哈希,即使它们不相同。我相信一个成熟的实现必须考虑到平等和哈希。
        【解决方案10】:

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

        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 协议的东西。

        【讨论】:

        • 请注意,此实现保留每个重复项的第一次出现。
        【解决方案11】:

        从 NSObject 派生的类的特殊情况

        鉴于 NSObject 中默认的 Equitable (& Hashable) 一致性基本上是垃圾,您最好确保提供正确的

        static func == (lhs: YourClassDerivedFromNSObject, rhs: YourClassDerivedFromNSObject) -> Bool {
        

        实现,以免你想要拔出插入的重复项 设置

        【讨论】:

          猜你喜欢
          • 2020-01-25
          • 1970-01-01
          • 2015-12-27
          • 2016-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-21
          • 1970-01-01
          相关资源
          最近更新 更多