— Swift, Functional Programming — 2 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.
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.
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.
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: String3 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.
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.
compactMap
is a versatile function that combines transformation and filtering in a single operation.nil
values.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.