brightec
Home Services Projects Blog About Contact

Mapping, Filtering and Reducing | Swift Basics

Swift basics 003

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

Map

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.

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)).

Filter

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 }

Reduce

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)

Looking for something else?

Search over 200 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!