假设我有一个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]
?
答案 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,则将其添加到结果中。
(请注意,这与其他语言中 Rob Mayoff指出,如果Optionals是SequenceTypes,他们可能应该这样,这将是一个明智的名称。)flatMap
的含义不符,甚至与Swift中flatMap
的其他含义不匹配。一个更好的名字本来就是mapOptional
或mapSome
。但它仍然是直观的,即使不一致。它“映射到选项,然后展平所有的nils。”
答案 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