Replacing flatMap With compactMap

Swift 4.1 ships with the (still in beta) Xcode 9.3 and brings more changes to the Swift language and the Swift standard library. Apple intends it as a source compatible upgrade to Swift 4.0 but I have hit one source code change that I am guessing will be widespread.

Using flatMap on a sequence (like an Array) filtering anything that maps to nil is now deprecated and replaced by compactMap.

The Short Version Of What Is Changing

Are you using flatMap to remove nil items from an array of optionals:

let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let valid = names.flatMap { $0 }
// ["Tom", "Peter", "Harry"]

Xcode 9.3 shows a deprecation warning for using flatMap this way:

Xcode flatMap deprecation message

The suggested fix from Xcode renames flatMap to compactMap:

let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let valid = names.compactMap { $0 }
// ["Tom", "Peter", "Harry"]

This applies anytime you use flatMap on a sequence with a closure that returns an optional. So this is also deprecated:

let words = ["53", "nine", "hello","0"]
let values = words.flatMap { Int($0) }

Replacing flatMap with compactMap removes the deprecation warning:

let values = words.compactMap { Int($0) } // Returns [Int]
// [53, 0]

Tell Me More

First of all, Swift 4.1 does not deprecate all uses of flatMap - only one case is changing. Swift 4.0 has three situations where you can use flatMap:

  1. Using flatMap on a sequence with a closure that returns a sequence:

    Sequence.flatMap<S>(_ transform: (Element) -> S)
     -> [S.Element] where S : Sequence
    

    I think this was probably the first use of flatMap I came across in Swift. Use it to apply a closure to each element of a sequence and flatten the result:

    let scores = [[5,2,7], [4,8], [9,1,3]]
    let allScores = scores.flatMap { $0 }
    // [5, 2, 7, 4, 8, 9, 1, 3]
    
    let passMarks = scores.flatMap { $0.filter { $0 > 5} }
    // [7, 8, 9]
    

    Swift 4.1 does not change this use of flatMap.

  2. Using flatMap on an optional

    The closure takes the non-nil value of the optional and returns an optional. If the original optional is nil then flatMap returns nil:

    Optional.flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
    
    let input: Int? = Int("8")
    let passMark: Int? = input.flatMap { $0 > 5 ? $0 : nil}
    // Optional(8)
    

    Swift 4.1 does not change this use of flatMap.

  3. Using flatMap on a sequence with a closure that returns an optional.

    Sequence.flatMap<U>(_ transform: (Element) -> U?) -> U?
    

    This is the use of flatMap that Swift 4.1 (Xcode 9.3) replaces with compactMap.

    let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
    let counts = names.compactMap { $0?.count }
    // [3, 5, 5]
    

What’s The Point?

In summary it seems to come down to discouraging the misuse of flatMap when a plain map will do the job:

let myNames: [String] = ["John", "Joe", "Jack"]

// No need to flatMap (or compactMap)
let myCounts = myNames.flatMap { $0.count }
// [4, 3, 4]

// map is enough
let myCounts = myNames.map { $0.count }
// [4, 3, 4]

The idea of the name change to compactMap is to better describe what the function does. Mapping over a sequence and then compacting by removing the nil elements from the result. Future versions of Swift might also gives us a compact function for the common situation of removing nil values from a sequence

Further Reading

Never miss a post!

iOS Size Classes Cheat Sheet

Subscribe and get my free iOS Size Classes Cheat Sheet

Success! Now check your email to confirm your subscription and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
No time to watch WWDC videos?

Sign up to get my iOS posts direct to your inbox and I will send you a free PDF of my iOS Size Classes Cheat Sheet.

OK! Check your inbox (or spam folder) for an email to confirm your details and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
Archives Categories