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]
よりも)簡単な方法はありますか?
素敵なワンライナーにはflatMap
を使用することをお勧めします。
let strings = grabBag.flatMap { $0 as? String }
これで、strings
のタイプは[String]
。
更新:Swift 4.2では、compactMap
の代わりにflatMap
を使用する必要があります。
let strings = grabBag.compactMap { $0 as? String }
これがflatMap
の目的です。
let strings = grabBag.flatMap{ $0 as? String }
これは、オプションを返すクロージャを取ります。オプションがnil以外の場合、結果に追加されます。
((これは他の言語の Rob Mayoffは、OptionalsがSequenceTypesである場合、おそらくそうあるべきであると述べています。これは賢明な名前です。)flatMap
の意味とは一致せず、SwiftのflatMap
の他の意味とも一致しないことに注意してください。より適切な名前はmapOptional
またはmapSome
でした。しかし、一貫性がなくても、それでも直感的です。 「オプションにマップしてから、すべてのnilをフラット化します」。
テスト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
とにかく、ロブ・ネイピアも答えたように、grabBag.flatMap { $0 as? String }
は短く、おそらく単純です。
これは小さなSwift 5 ayaioの答えに基づく配列拡張です。タイプでたくさんフィルタリングしたいが、毎回クロージャーを書きたくない場合に役立つかもしれません。
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"]