Home > Articles > Programming > General Programming/Other Languages

  • Print
  • + Share This
This chapter is from the book

Optional Mapping

Swift’s map and flatMap functions enable you to conditionally apply functions to optional values. Their calls are similar, as you see in the following declarations, and both are incredibly useful tools:

/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
func map<U>(f: @noescape (T) -> U) -> U?
/// Returns `f(self)!` iff `self` and `f(self)` are not nil.
func flatMap<U>(f: @noescape (T) -> U?) -> U?

The map closures return type U, which may or may not be an optional, while flatMap closures specifically return type U?, which is always an optional. This practical limitation simply means you can use map with closures that return non-optionals, but you cannot do the same with flatMap:

// flatMap must return optional
print(word.map({string->String in string})) // compiles
// print(word.flatMap({string->String in string})) // errors

Maps and Chaining

When working with optionals, map and flatMap both act like chaining, but you supply an arbitrary closure instead of a chained method name or property:

var word: String? = "hello"
var cap = word?.capitalizedString // Optional("Hello")
cap = word.map({$0.capitalizedString}) // Optional("Hello")

When you just want to unwrap-and-apply, use map. This mapping:

UIImage(named:"ImageName").map({print($0.size)})

is equivalent to this if-let:

if let image = UIImage(named:" ImageName ") {
    print(image.size)
}

Both the mapping and if-let include about the same level of code complexity for this particular example. Both unwrap the optional returned by UIImage(named:) and then print the size. You can argue which approach is better. Both bind the unwrapped result to a local constant whether that constant does or does not have an explicit name.

Filtering nil Values with flatMap

The flatMap function offers great utility both in the realm of optionals and outside it. With optionals, you can use flatMap to filter away nil values and easily convert an array of optionals to an array of unwrapped values:

let optionalNumbers: [Int?] = [1, 3, 5, nil, 7, nil, 9, nil]
let nonOptionals = optionalNumbers.flatMap({$0})
print(nonOptionals) // [1, 3, 5, 7, 9]

Recipe 3-3 uses a single flatMap call to eliminate nil instances and extract values from their optional wrappers.

Recipe 3-3 Extracting Members from an Optionals Array

func flatMembers<T>(array: [T?]) -> [T] {
    return array.flatMap({$0})
}
  • + Share This
  • 🔖 Save To Your Account