按类型过滤 Swift [AnyObject] 数组

问题描述 投票:0回答:6

假设我有一系列

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]

arrays swift casting
6个回答
60
投票

最好使用

compactMap
来制作漂亮的单行:

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

现在

strings
属于类型
[String]


8
投票

这就是

flatMap
的用途:

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

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

(请注意,这与其他语言中

flatMap
的含义不匹配,甚至与 Swift 中
flatMap
的其他含义也不匹配。更好的名称应该是
mapOptional
mapSome
。但即使不一致,它仍然很直观。它“映射到可选值,然后展平所有零。”
Rob Mayoff 指出,如果可选值是 SequenceTypes(它们可能应该是),这将是一个明智的名称。)


5
投票

我想说测试 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 }
更短,也许更简单。


4
投票

这是一个基于 ayaio 答案的小型 Swift 5 数组扩展。如果您想经常按类型过滤并且不想每次都编写闭包,那么可能会很有用。

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"]

1
投票

适用于任何序列的改进扩展:

extension Sequence {
    func filter<T>(by: T.Type) -> [T] {
        compactMap{$0 as? T}
    }
}

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
© www.soinside.com 2019 - 2024. All rights reserved.