Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use swift flatMap to filter out optionals from an array

Tags:

I'm a little confused around flatMap (added to Swift 1.2)

Say I have an array of some optional type e.g.

let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5] 

In Swift 1.1 I'd do a filter followed by a map like this:

let filtermap = possibles.filter({ return $0 != nil }).map({ return $0! }) // filtermap = [1, 2, 3, 4, 5] 

I've been trying to do this using flatMap a couple ways:

var flatmap1 = possibles.flatMap({     return $0 == nil ? [] : [$0!] }) 

and

var flatmap2:[Int] = possibles.flatMap({     if let exercise = $0 { return [exercise] }     return [] }) 

I prefer the last approach (because I don't have to do a forced unwrap $0!... I'm terrified for these and avoid them at all costs) except that I need to specify the Array type.

Is there an alternative away that figures out the type by context, but doesn't have the forced unwrap?

like image 464
MathewS Avatar asked Apr 25 '15 20:04

MathewS


1 Answers

Since Swift 4.1 you can use compactMap:

let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5] let actuals = possibles.compactMap { $0 } 

(Swift 4.1 replaced some overloads of flatMap with compactmap. If you are interested in more detail on this then see for example: https://useyourloaf.com/blog/replacing-flatmap-with-compactmap/ )

With Swift 2 b1, you can simply do

let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5] let actuals = possibles.flatMap { $0 } 

For earlier versions, you can shim this with the following extension:

extension Array {     func flatMap<U>(transform: Element -> U?) -> [U] {         var result = [U]()         result.reserveCapacity(self.count)         for item in map(transform) {             if let item = item {                 result.append(item)             }         }         return result     } } 

One caveat (which is also true for Swift 2) is that you might need to explicitly type the return value of the transform:

let actuals = ["a", "1"].flatMap { str -> Int? in     if let int = str.toInt() {         return int     } else {         return nil     } } assert(actuals == [1]) 

For more info, see http://airspeedvelocity.net/2015/07/23/changes-to-the-swift-standard-library-in-2-0-betas-2-5/

like image 141
Fizker Avatar answered Oct 04 '22 14:10

Fizker