【问题标题】:Extend Swift Array to Filter Elements by Type扩展 Swift 数组以按类型过滤元素
【发布时间】:2015-10-13 20:33:26
【问题描述】:

如何扩展 swift 数组以访问特定类型的成员?

如果数组包含从同一个超类继承的多个类的实例,则这是相关的。理想情况下,它会适当地执行类型检查。


一些不太奏效的想法和事情:

使用filter(_:) 方法可以正常工作,但会强制执行类型安全。例如:

protocol MyProtocol { }
struct TypeA: MyProtocol { }
struct TypeB: MyProtocol { }

let myStructs:[MyProtocol] = [ TypeA(), TypeA(), TypeB() ]

let filteredArray = myStructs.filter({ $0 is TypeA })

filteredArray 包含正确的值,但类型仍然是 [MyProtocol] 而不是 [TypeA]。我希望用let filteredArray = myStructs.filter({ $0 is TypeA }) as! [TypeA] 替换最后一个可以解决这个问题,但是该项目以EXEC_BAD_INSTRUCTION 失败,我不明白。也许类型转换数组是不可能的?

理想情况下,这种行为可以包含在数组扩展中。以下内容无法编译:

extension Array {
    func objectsOfType<T:Element>(type:T.Type) -> [T] {
        return filter { $0 is T } as! [T]
    }
}

这里似乎至少存在两个问题:类型约束T:Element 似乎不起作用。我不确定基于泛型类型添加约束的正确方法是什么。我的意思是说TElement 的子类型。此外,第 3 行存在编译时错误,但这可能只是相同的错误传播。

【问题讨论】:

    标签: arrays swift generics swift-extensions


    【解决方案1】:

    SequenceType 有一个 flatMap() 方法,它充当“可选过滤器”:

    extension SequenceType {
        /// Return an `Array` containing the non-nil results of mapping
        /// `transform` over `self`.
        ///
        /// - Complexity: O(*M* + *N*), where *M* is the length of `self`
        ///   and *N* is the length of the result.
        @warn_unused_result
        @rethrows public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
    }
    

    结合matt的建议用as?代替is你 可以作为

    let myStructs:[MyProtocol] = [ TypeA(), TypeA(), TypeB() ]
    let filteredArray = myStructs.flatMap { $0 as? TypeA }
    

    现在filteredArray 的类型被推断为[TypeA]

    作为一种扩展方法

    extension Array {
        func objectsOfType<T>(type:T.Type) -> [T] {
            return flatMap { $0 as? T }
        }
    }
    
    let filteredArray = myStructs.objectsOfType(TypeA.self)
    

    注意:对于 Swift >= 4.1,flatMap 替换为 compactMap

    【讨论】:

      【解决方案2】:

      代替测试(使用is)如何铸造(使用as)?

      let myStructs:[MyProtocol] = [ TypeA(), TypeA(), TypeB() ]
      var filteredArray = [TypeA]()
      for case let t as TypeA in myStructs {filteredArray.append(t)}
      

      【讨论】:

      • 请注意:这是 Swift 2.0。
      • 或:let filteredArray = myStructs.flatMap { $0 as? TypeA }
      • @MartinR 是的,我总是忘记flatMap 作为一种过滤形式。它的名字很糟糕。我实际上已经提交了一个错误报告,要求将其删除,因为它所做的一切现在都可以通过其他方式更清楚地完成。
      • @MartinR 万岁! flatMap(_:) 工作得很好。这对我来说并不突出。
      • @MartinR 您应该将其作为单独的答案输入。
      【解决方案3】:

      转换数组在 Swift 中不起作用。这是因为 Swift 中的数组使用泛型,就像您不能强制转换自定义类一样,只有类型 T 会发生变化。 (class Custom&lt;T&gt;, Custom&lt;Int&gt;() as! Custom&lt;String&gt;)。

      您可以做的是为 Array 创建一个扩展方法,您可以在其中定义这样的方法:

      extension Array {
          func cast<TOut>() -> [TOut] {
              var result: [TOut] = []
              for item in self where item is TOut {
                  result.append(item as! TOut)
              }
              return result
          }
      }
      

      【讨论】:

      • 知道了。使用像.flatMap(_:) 这样创建一个新数组的更实用的方法可以避免这种情况。数组类型之间的强制转换是否有原因?
      • 数组类型之间的转换是一项深度操作,它需要验证每个元素是否具有适当的类型以保持类型安全。我的猜测是他们只是不想隐藏这种命中率。
      【解决方案4】:

      我认为规范的 FP 答案是使用过滤器,就像你一样,结合 map:

      let filteredArray = myStructs.filter({ $0 is TypeA }).map({ $0 as! TypeA })
      

      或者,您可以使用 reduce:

      let filtered2 = myStructs.reduce([TypeA]()) {
          if let item = $1 as? TypeA {
              return $0 + [item]
          } else {
              return $0
          }
      }
      

      或者,因为它改变了一个数组,所以对 FP 不太友好:

      let filtered3 = myStructs.reduce([TypeA]()) { ( var array, value )  in
          if let item = value as? TypeA {
              array.append(item)
          }
          return array
      }
      

      实际上可以再次缩短为对 FP 友好的平面地图:

      let filtered4 = myStructs.flatMap { $0 as? TypeA }
      

      并将其放在扩展名中:

      extension Array {
          func elementsWithType<T>() -> [T] {
              return flatMap { $0 as? T }
          }
      }
      
      let filtered5 : [TypeA] = myStructs.elementsWithType()
      

      【讨论】:

      • 这当然归结为马特和马丁得出的答案,我把一切都搞砸了:)
      猜你喜欢
      • 1970-01-01
      • 2017-01-04
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2016-09-04
      • 1970-01-01
      • 2020-03-17
      • 2022-12-03
      相关资源
      最近更新 更多