2015-11-03 21 views
11

Dire che ho un array di AnyObject.Filtraggio di una matrice Swift [AnyObject] per tipo

let grabBag: [AnyObject] = [ "Tom", 4, "Dick", NSObject(), "Harry" ] 

e voglio lanciare a una serie di Strings, estraendo solo quegli elementi che sono in realtà Strings. Mi aspetterei questo lavoro:

let strings = grabBag.filter{ $0 is String } as! [String]  // 1 

ma dà l'errore 'Bool' is not convertible to 'String'. Eppure, questo funziona:

let definitelyStrings = grabBag.filter{ $0 is String }   // 2 
let strings = definitelyStrings as! [String]     // 

Perché 2 lavoro quando 1 non lo fa? E c'è un modo più semplice (rispetto allo 2) di estrarre e trasmettere gli elementi di un [AnyObject] a qualsiasi [T]?

+0

Nell'esempio 2 non è necessaria la seconda riga: 'let strings = grabBag.filter {$ 0 is String}' è sufficiente. Nessun casting forzato. Dimostralo con 'strings is [String]' che restituisce true – vadian

+2

@vadian 'let strings = grabBag.filter {$ 0 is String}' restituisce '[AnyObject]' con l'array 'grabBag' di OP, non' [String] '. – Moritz

+0

@EricD: Ma puoi fare 'grabBag.filter {$ 0 is String} .map {$ 0.lowercaseString}' che normalmente non è possibile con '[AnyObject]' – vadian

risposta

15

è meglio usare flatMap per una bella linea singola:

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

Ora strings è di tipo [String].

6

Questo è ciò che è per flatMap:

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

Questo richiede una chiusura che restituisce un optional; se l'opzione è non nulla, viene aggiunta al risultato.

( Si noti che questo non corrisponde al significato di flatMap da altre lingue, e non ha nemmeno corrisponde l'altro significato di flatMap a Swift. Un nome migliore sarebbe stata mapOptional o mapSome. Ma è ancora un po ' intuitiva, anche se incoerente. It "mappe di optional, poi si appiattisce tutte le nils". Rob Mayoff osserva che se Optionals erano SequenceTypes, che probabilmente dovrebbe essere, questo sarebbe un nome sensato.)

+1

La 'flatMap' di Scala funziona con la sua' Opzione' allo stesso modo di 'flatMap' di Swift con il suo' Opzionale'. Ma Scala lo ottiene "gratuitamente" perché "Option" di Scala è un tipo di raccolta completamente attraversabile (che contiene 0 o 1 elementi). Purtroppo, 'Optional' di Swift non è (attualmente) conforme a' SequenceType'. –

+0

@robmayoff Ho letto male la firma di Scala. È 'flatMap [B] (f: (A) ⇒ GenTraversableOnce [B]): List [B]'. Ho pensato che fosse 'flatMap [B] (f: (A) ⇒ List [List [B]]): List [B]'. OK; Lo comprerò. –

3

Direi che il test 1 in mancanza è chiaramente un bug del compilatore. In realtà si blocca nella 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 

Comunque, come Rob Napier anche risposto, grabBag.flatMap { $0 as? String } è più breve e forse più semplice.

Problemi correlati