按类型过滤Swift [AnyObject]数组

时间:2015-11-03 17:05:37

标签: arrays swift casting

假设我有一个AnyObject数组。

let grabBag: [AnyObject] = [ "Tom", 4, "Dick", NSObject(), "Harry" ]

我希望将它转换为Strings数组,只提取那些实际上是Strings的元素。我希望这可行:

let strings = grabBag.filter{ $0 is String } as! [String]      // 1

但它会给出错误'Bool' is not convertible to 'String'。然而这很有效:

let definitelyStrings = grabBag.filter{ $0 is String }         // 2
let strings = definitelyStrings as! [String]                   //

2没有时,为什么1有效?是否有一种更简单(比2)方法可以将[AnyObject]的元素提取并转换为[T]

5 个答案:

答案 0 :(得分:27)

最好使用flatMap作为一个好的单行代码:

let strings = grabBag.flatMap { $0 as? String }

现在strings的类型为[String]

更新:在Swift 4.2中,您应该使用compactMap代替flatMap

let strings = grabBag.compactMap { $0 as? String }

答案 1 :(得分:7)

这是flatMap的用途:

let strings = grabBag.flatMap{ $0 as? String }

这需要一个返回可选的闭包;如果optional是非nil,则将其添加到结果中。

请注意,这与其他语言中flatMap的含义不符,甚至与Swift中flatMap的其他含义不匹配。一个更好的名字本来就是mapOptionalmapSome。但它仍然是直观的,即使不一致。它“映射到选项,然后展平所有的nils。” Rob Mayoff指出,如果Optionals是SequenceTypes,他们可能应该这样,这将是一个明智的名称。)

答案 2 :(得分:3)

我说测试1失败显然是编译器错误。实际上它在REPL中崩溃了:

Welcome to Apple Swift version 2.0 (700.1.100.2 700.1.74). Type :help for assistance.
  1> import Foundation
  2> let grabBag: [AnyObject] = [ "Tom", 4, "Dick", NSObject(), "Harry" ]
grabBag: [AnyObject] = 5 values {
  [0] = "Tom"
  [1] = Int64(4)
  [2] = "Dick"
  [3] = {
    isa = NSObject
  }
  [4] = "Harry"
}
  3> let strings = grabBag.filter { $0 is String } as! String
strings: String = {
  _core = {
    _baseAddress =
    _countAndFlags =
    _owner = <extracting data from value failed>

  }
}
Execution interrupted. Enter Swift code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
4> :bt
* thread #1: tid = 0x501bac, 0x00000001005c41f4 $__lldb_expr12`main + 420 at repl.swift:3, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  * frame #0: 0x00000001005c41f4 $__lldb_expr12`main + 420 at repl.swift:3
    frame #1: 0x0000000100001420 repl_swift`_mh_execute_header + 5152
    frame #2: 0x00007fff8dd725c9 libdyld.dylib`start + 1
    frame #3: 0x00007fff8dd725c9 libdyld.dylib`start + 1

无论如何,正如Rob Napier所回答的那样,grabBag.flatMap { $0 as? String }更短,也许更简单。

答案 3 :(得分:0)

这是一个很小的基于ayaio答案的Swift 5 Array扩展。如果您想按类型进行大量过滤并且不想每次都写一个闭包,则可能很有用。

extension Array {
   func filteredByType<T> (_: T.Type) -> [T] {
       return compactMap({ (element) in
           return element as? T
       })
   }
}

用法示例:

let array: [Any] = ["foo", 47, ["baz"], "bar"]
let stringArray: [String] = array.filteredByType(String.self)
print(stringArray) // ["foo", "bar"]

答案 4 :(得分:0)

享受

extension Array {
    func filtered<T>(by _: T.Type) -> [T] {
        return filter { type(of: $0) == T.self } as! [T]
   }
}

用法示例:

    view.addSubview(UIView())
    view.addSubview(UILabel())
    view.addSubview(UIImageView())
    view.addSubview(UIView())
    
    let allUIViewsInSubviews      = view.subviews.filtered(by: UIView.self)
    let allUILabelsInSubviews     = view.subviews.filtered(by: UILabel.self)
    let allUIImageViewsInSubviews = view.subviews.filtered(by: UIImageView.self)
    print(allUIViewsInSubviews.count) // 2
    print(allUILabelsInSubviews.count) // 1
    print(allUIImageViewsInSubviews.count) // 1