无法在 Swift 数组扩展中使用包含

Posted

技术标签:

【中文标题】无法在 Swift 数组扩展中使用包含【英文标题】:Unable to use contains within a Swift Array extension 【发布时间】:2014-06-06 21:41:52 【问题描述】:

我正在尝试编写一个提供“不同”方法的简单数组扩展。这是我目前所拥有的:

extension Array 
  func distinct() -> T[] 
    var rtn = T[]()

    for x in self 
      var containsItem = contains(rtn, x)
      if !containsItem 
        rtn.append(x)
      
    
    return rtn
  
 

问题是'contains'语句失败如下:

找不到接受所提供参数的“包含”的重载

我很确定类型约束是正确的。有什么想法吗?

【问题讨论】:

【参考方案1】:

斯威夫特 1.x

array 中的元素不必是Equatable,即它们不能与== 相提并论。

这意味着您不能为所有个可能的数组编写该函数。而且 Swift 不允许你只扩展数组的一个子集。

这意味着你应该把它写成一个单独的函数(这可能就是为什么contains 也不是一个方法)。

let array = ["a", "b", "c", "a"]

func distinct<T: Equatable>(array: [T]) -> [T] 
    var rtn = [T]()

    for x in array 
        var containsItem = contains(rtn, x)
        if !containsItem 
            rtn.append(x)
        
    
    return rtn


distinct(array) // ["a", "b", "c"]

Swift 2/Xcode 7(测试版)更新

Swift 2 支持将扩展限制为协议实现的子集,因此现在允许以下操作:

let array = ["a", "b", "c", "a"]

extension SequenceType where Generator.Element: Comparable 
    func distinct() -> [Generator.Element] 
        var rtn: [Generator.Element] = []

        for x in self 
            if !rtn.contains(x) 
                rtn.append(x)
            
        
        return rtn
    


array.distinct() // ["a", "b", "c"]

注意苹果如何使用相同的语法添加SequenceType.contains

【讨论】:

如果数组的类型不符合Equatable,您能否引发异常或以某种方式返回 nil?这将允许扩展 Array,并优雅地处理不受支持的类型。 @AlexWayne 你可以通过对新的本地模板类型进行可选转换来让它静默失败(我不会说它是优雅的)。您可以在我的答案here 上看到一个示例【参考方案2】:

终于知道怎么弄了:

extension Array 
    func contains<T : Equatable>(obj: T) -> Bool 
        return self.filter($0 as? T == obj).count > 0
    

    func distinct<T : Equatable>(_: T) -> T[] 
        var rtn = T[]()

        for x in self 
            if !rtn.contains(x as T) 
                rtn += x as T
            
        

        return rtn
    

以及使用/测试:

let a = [ 0, 1, 2, 3, 4, 5, 6, 1, 2, 3 ]

a.contains(0)
a.contains(99)

a.distinct(0)

不幸的是,我无法找到一种方法来做到这一点,而不必指定随后被忽略的参数。它存在的唯一原因是调用 distinct 的正确形式。对于distinct,这种方法的主要优势似乎在于它不会将distinct 之类的常用术语转储到全局命名空间中。对于 contains 情况,它看起来确实更自然。

【讨论】:

是的。不幸的是,根据设计,这允许您调用诸如a.distinct("a") 之类的不合逻辑的东西,而对于该函数,编译器可能会给您一个很好的错误。情况总是如此,即使有办法摆脱参数,因为两个 Ts 不会强制匹配。 @nschum 在我的测试中,我无法输入错误的类型。 真的吗?对我来说 [0, 1].distinct("a") 在运行时会导致 EXC_BAD_ACCESS。 @nschum 嗯......我认为我至少尝试过,但现在我似乎看到了同样的事情。不知道:) 如果它在运行时崩溃,那么类型安全的意义何在 :)【参考方案3】:

另一种解决方案是使用 find(Array:[T], obj:T) 函数。它会返回一个可选的 Int,所以你可以做的是

if let foundResult = find(arr, obj) as Int

     //obj is contained in arr
 else

     //obj is not contained in arr.

【讨论】:

【参考方案4】:

从 Swift 2 开始,这可以通过协议扩展方法来实现, 例如在所有符合SequenceType 的类型上,其中序列 元素符合Equatable:

extension SequenceType where Generator.Element : Equatable 

    func distinct() -> [Generator.Element] 
        var rtn : [Generator.Element] = []

        for elem in self 
            if !rtn.contains(elem) 
                rtn.append(elem)
            
        

        return rtn
    

例子:

let items = [1, 2, 3, 2, 3, 4]
let unique = items.distinct()
print(unique) // [1, 2, 3, 4]

如果元素被进一步限制为Hashable,那么你 可以利用Set 类型:

extension SequenceType where Generator.Element : Hashable 

    func distinct() -> [Generator.Element] 
        return Array(Set(self))
    

【讨论】:

以上是关于无法在 Swift 数组扩展中使用包含的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中扩展 NSManagedObject 以包含 MKAnnotation?

在 Swift 中继承具有自己类型和存储属性的数组

如何在swift中过滤包含属性的数组

在 Swift 中使用 Alamofire 发送包含字典对象数组的字典对象时会发生奇怪的行为?

准备 segue 数组时不包含数据(使用 swift)

尽管包含更多条目,但数组仅在 for-loop 之后向 Struct 添加单个条目 - Swift/Xcode