Je suis un peu confus par flatMap (ajouté à Swift 1.2)
Disons que j'ai un tableau de type facultatif, par exemple.
let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5]
Dans Swift 1.1, je ferais un filtre suivi d'une carte comme celle-ci:
let filtermap = possibles.filter({ return $0 != nil }).map({ return $0! })
// filtermap = [1, 2, 3, 4, 5]
J'ai essayé de le faire en utilisant flatMap de plusieurs manières:
var flatmap1 = possibles.flatMap({
return $0 == nil ? [] : [$0!]
})
et
var flatmap2:[Int] = possibles.flatMap({
if let exercise = $0 { return [exercise] }
return []
})
Je préfère la dernière approche (car je n'ai pas à décompresser de force $0!
... je suis terrifié pour cela et à les éviter à tout prix) sauf que je dois spécifier le type Array.
Existe-t-il une alternative qui détermine le type en fonction du contexte, mais qui n’a pas de décompression forcée?
J'aime toujours la première solution, qui crée un seul tableau intermédiaire Il peut être légèrement plus compact être écrit comme
let filtermap = possibles.filter({ $0 != nil }).map({ $0! })
Mais flatMap()
sans annotation de type et sans décompression forcée .__ est possible:
var flatmap3 = possibles.flatMap {
flatMap($0, { [$0] }) ?? []
}
flatMap
extérieur est la méthode tableau
func flatMap<U>(transform: @noescape (T) -> [U]) -> [U]
et la flatMap
intérieure est la fonction
func flatMap<T, U>(x: T?, f: @noescape (T) -> U?) -> U?
Voici une comparaison simple des performances (compilée en mode Release) . Elle montre que la première méthode est plus rapide, d’un facteur .__ environ de 10:
let count = 1000000
let possibles : [Int?] = map(0 ..< count) { $0 % 2 == 0 ? $0 : nil }
let s1 = NSDate()
let result1 = possibles.filter({ $0 != nil }).map({ $0! })
let e1 = NSDate()
println(e1.timeIntervalSinceDate(s1))
// 0.0169369578361511
let s2 = NSDate()
var result2 = possibles.flatMap {
flatMap($0, { [$0] }) ?? []
}
let e2 = NSDate()
println(e2.timeIntervalSinceDate(s2))
// 0.117663979530334
Relatif à la question. Si vous appliquez flatMap
à un tableau facultatif, n'oubliez pas de forcer ou de forcer le désenroulement de votre tableau, sinon il appellera flatMap
sur Optional
et non des objets conformes au protocole Sequence
. J'ai fait cette erreur une fois, par exemple. lorsque vous souhaitez supprimer des chaînes vides:
var texts: [String]? = ["one", "two", "", "three"] // has unwanted empty string
let notFlatMapped = texts.flatMap({ $0.count > 0 ? $0 : nil })
// ["one", "two", "", "three"], not what we want - calls flatMap on Optional
let flatMapped = texts?.flatMap({ $0.count > 0 ? $0 : nil })
// ["one", "two", "three"], that's what we want, calls flatMap on Array
Vous pouvez utiliser reduce
:
let flattened = possibles.reduce([Int]()) {
if let x = $1 { return $0 + [x] } else { return $0 }
}
Vous déclarez toujours le type, mais il est légèrement moins gênant.
Comme c'est quelque chose qui me semble faire beaucoup, j'explore une fonction générique pour le faire.
J'ai essayé d'ajouter une extension à Array pour pouvoir faire quelque chose comme possibles.unwraped
, mais je ne savais pas comment créer une extension sur un tableau. Au lieu de cela utilisé un opérateur personnalisé - la partie la plus difficile ici était de savoir quel opérateur choisir. En fin de compte, j'ai choisi >!
pour montrer que le tableau est filtré >
puis non enveloppé !
.
let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5]
postfix operator >! {}
postfix func >! <T>(array: Array<T?>) -> Array<T> {
return array.filter({ $0 != nil }).map({ $0! })
}
possibles>!
// [1, 2, 3, 4, 5]