Filtering a Swift [AnyObject] array by type
It's better to use compactMap
for a nice one-liner:
let strings = grabBag.compactMap { $0 as? String }
Now strings
is of type [String]
.
I'd say that test 1 failing is clearly a compiler bug. In fact it crashes in the 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
Anyway, as Rob Napier also answered, grabBag.flatMap { $0 as? String }
is shorter and maybe simpler.
This is what flatMap
is for:
let strings = grabBag.flatMap{ $0 as? String }
This takes a closure that returns an optional; if the optional is non-nil, then it is added to the result.
(Note that this doesn't match the meaning of Rob Mayoff notes that if Optionals were SequenceTypes, which they probably should be, this would be a sensible name.)flatMap
from other languages, and doesn't even match the other meaning of flatMap
in Swift. A better name would have been mapOptional
or mapSome
. But it's still kind of intuitive, even if inconsistent. It "maps to optionals, then flattens out all the nils."