Skip to content
DeveloperMemos

Using Swift's compactMap

Swift, Functional Programming2 min read

Swift's compactMap is a versatile function that allows you to transform and filter elements in a collection, all in one go. It combines the functionality of both map and filter in a concise and expressive manner. By using compactMap, you can perform transformations on your data while simultaneously eliminating any nil values that might arise during the transformation process. In this article, we'll delve into the intricacies of compactMap and demonstrate its usage through some examples.

What is compactMap?

The compactMap function was introduced in Swift 4.1 as an extension on the Sequence protocol. It provides a powerful way to apply a transformation to each element in a collection and filter out any resulting nil values. The resulting collection is a compacted version of the original, containing only non-nil transformed values.

The signature of the compactMap function looks like this:

1func compactMap<ElementOfResult>(
2 _ transform: (Element) throws -> ElementOfResult?
3) rethrows -> [ElementOfResult]

The transform closure takes an element of the collection as input and returns an optional value of a potentially different type. Any nil results are filtered out, and the non-nil values are collected into an array.

Transforming and Filtering with compactMap

Let's start by considering a simple example. Imagine you have an array of strings that represent numbers, but some of them cannot be converted to Int. You want to convert the valid strings to their integer representations, discarding any non-convertible strings. This is where compactMap shines.

1let stringNumbers = ["1", "2", "3", "four", "5", "six"]
2let integerNumbers = stringNumbers.compactMap { Int($0) }
3
4print(integerNumbers) // Output: [1, 2, 3, 5]

In this example, we use compactMap to convert each string in stringNumbers to an Int. The closure Int($0) attempts to convert each string to an integer. If the conversion succeeds, the closure returns the integer value wrapped in an optional. If the conversion fails, the closure returns nil. The compactMap function filters out the nil values, resulting in an array containing only the valid integer representations.

Custom Transformations with compactMap

The transform closure in compactMap can perform any custom transformation you require. It's not limited to simple type conversions. Let's say you have an array of user objects, and you want to extract all the usernames from that array.

1struct User {
2 let name: String
3 let username: String?
4}
5
6let users = [
7 User(name: "John", username: "johnny"),
8 User(name: "Jane", username: nil),
9 User(name: "Alice", username: "alice29")
10]
11
12let usernames = users.compactMap { $0.username }
13
14print(usernames) // Output: ["johnny", "alice29"]

In this example, the users array contains User objects, some of which have a valid username

and others with nil usernames. By using compactMap with the closure $0.username, we extract the usernames into a new array, filtering out any nil values.

Transforming and Filtering Complex Structures

compactMap is not limited to working with simple arrays. It can be used with more complex data structures as well. For example, consider a dictionary that maps names to addresses, where some addresses might be missing.

1let addresses: [String: String?] = [
2 "John": "123 Main St",
3 "Jane": nil,
4 "Alice": "456 Elm St"
5]
6
7let validAddresses = addresses.compactMap { $0.value }
8
9print(validAddresses) // Output: ["123 Main St", "456 Elm St"]

In this case, the addresses dictionary maps names to optional address values. We use compactMap to extract the non-nil address values, resulting in an array that contains only the valid addresses.

Summary

  • compactMap is a versatile function that combines transformation and filtering in a single operation.
  • It allows us to transform elements in a collection while discarding any resulting nil values.
  • The resulting collection contains only non-nil transformed values, providing a compacted representation of the original data.

By leveraging compactMap effectively, you can simplify your code, making it more concise and expressive.