假设我有一系列
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]
?
最好使用
compactMap
来制作漂亮的单行:
let strings = grabBag.compactMap { $0 as? String }
现在
strings
属于类型 [String]
。
这就是
flatMap
的用途:
let strings = grabBag.flatMap{ $0 as? String }
这需要一个返回可选值的闭包;如果可选值非零,则将其添加到结果中。
(请注意,这与其他语言中
flatMap
flatMap
的其他含义也不匹配。更好的名称应该是 mapOptional
或 mapSome
。但即使不一致,它仍然很直观。它“映射到可选值,然后展平所有零。”我想说测试 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 }
更短,也许更简单。
这是一个基于 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"]
适用于任何序列的改进扩展:
extension Sequence {
func filter<T>(by: T.Type) -> [T] {
compactMap{$0 as? T}
}
}
按类型过滤数组的扩展:
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