How to Filter, Map, and Reduce in Swift

the Swift logo on a yellow background

These are some of the common ways of working with arrays.

Learning to filter, map, and reduce in Swift can be challenging at first. However, there are some common ways of working in Swift that (with a little practice) can make this process easier. In this article, we share how you can use the map, reduce, and filter functions in Swift.

Introducing Map, Filter, and Reduce in Swift

Swift’s map, filter, and reduce functions are often referred to as higher-order functions. This is because they take a function as an input and return it as an output. Using map, filter, and reduce in Swift results in clean, concise code that is more readable and offers better results. This supports us in our endeavour to develop high-performance apps with a people-first approach.

The Mapping Process in Swift

Mapping is the process of converting an array from 1 type to another [A] to [B]. There are lots of reasons to do this, a simple example is extracting an array of user IDs from an array of users. In this example we're mapping from [User] to [Int]

struct User {
 let id: Int
 let name: String
}

let users = [User(id: 1, name: "Sulley"), User(id: 2, name: "Mike")]
let userIds = users.map { (user) in
 return user.id
}

This can be shortened to let userIds = users.map { $0.id } by making use of Swift's shorthand argument names.

Mapping with Key Paths

From Swift 5.2, we're now able to use key paths in place of the closures above, so we can change the map to let userIds = users.map(\.id)

Compact Map

compactMap is a special type of map that filters out any nil elements. This is helpful when your mapping function could fail and you want to ignore those failures e.g. converting an array of strings to an array of URLs (not all strings are valid URLs). Where map would convert from [String] to [URL?], compactMap will convert from [String] to [URL]

let strings = ["https://www.brightec.co.uk", "not a url"]
let urls = strings.map { URL(string: $0) } // [URL?]
let urls = strings.compactMap { URL(string: $0) } // [URL]

Flat Map

flatMap is another special type of map that is useful when:

  1. your mapping closure returns an array rather than an element, and
  2. you want your final array to be 1 single dimension rather than an array of arrays.

struct Article {
 let title: String
 let tags: [String]
}

let articles = [
 Article(title: "First blog", tags: ["swift", "ios"]),
 Article(title: "Second blog", tags: ["kotlin", "android"])
]

let allTags = articles.map(\.tags) // [["swift", "ios"], ["kotlin", "android"]], type is [[String]]
let allTags = articles.flatMap(\.tags) // ["swift", "ios", "kotlin", "android"], type is [String]

It's important to note that flatMap doesn't remove duplicates. If you want to remove duplicates, the easiest option may be to wrap it in a set Set(articles.flatMap(\.tags)).

Filtering

Filtering is as you'd expect - take an array and filter/remove items from it, returning another array of the same type. Whether an item is kept or removed is controlled by the closure that you define.

let numbers = [1, 2, 3, 4, 5]
let lessThan3 = numbers.filter { (number) in
 return number < 3
}

// Or using shorthand argument names
let lessThan3 = numbers.filter { $0 < 3 }

Reducing to a Single Value

reduce lets you combine the elements of an array into a single value. You give reduce an initial value and then the closure is called on each element in the array. The first element is given the initial value, the second element is given the result from the first element etc.

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { (total, number) in
 return total + number
}

print(sum) // prints 15

It's actually possible to simplify this specific example to just let sum = numbers.reduce(0, +). This is because + is defined as a function whose signature matches the requirements of the reduce closure (it takes 2 Int parameters and returns Int).

You can also use reduce where your initial value is more complicated. In this example, we start with an instance of a struct and reduce the array in to it by incrementing the relevant property.

let numbers = [1, 2, 3, 4, 5]
struct OddEven {
 var odd = 0
 var even = 0
}

let oddEven = numbers.reduce(into: OddEven()) { result, number in
 if number % 2 == 0 {
 result.even += 1
 } else {
 result.odd += 1
 }
}
print(oddEven) // prints OddEven(odd: 3, even: 2)

In Summary

We hope you’ve found this post helpful. Now you should know a little more about mapping, filtering, and reducing in Swift. We hope the knowledge proves useful - whether to impress a potential employer during a job interview or in your own learning and development.

Continue reading our Swift Basics series.


Looking for something else?

Search over 400 blog posts from our team

Want to hear more?

Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!