Swift's Array type is powerful and flexible. In this post, I show you a simple solution to find an object in an array. Fire up Xcode and create a playground if you want to follow along. Clear the contents of the playground and add an import statement for Foundation.

import Foundation

We start simple and define an array of integers. Notice that the array contains a few duplicates.

import Foundation

let numbers = [
    1,
    2,
    3,
    4,
    5,
    1,
    2,
    3
]

Swift 5

The API of Array has changed quite a bit over the years. This post uses Swift 5.3 and I won't cover any of the deprecated methods.

First Where

The Swift Standard Library defines a number of methods on Array to browse the items in an array. Let's take a look at the most obvious option first, the first(where:) method.

The method accepts a closure as its only argument. The closure accepts an object of type Int and its return value is Bool. The idea is simple. The first(where:) method returns the first item in the array for which the closure returns true. Let's try it out.

We want to find the first integer that is larger than 3. This is what that looks like in code.

let result = numbers.first(where: { value -> Bool in
    value > 3
})

We can improve the implementation by using Swift's trailing closure syntax.

let result = numbers.first { value -> Bool in
    value > 3
}

We can further simplify the implementation by making use of shorthand argument syntax. This looks much better.

let result = numbers.first { $0 > 3 }

It is important to know that the return type of the first(where:) method is of an optional type. It returns nil if the closure that is passed to the first(where:) method returns false for every item of the array. In the following example, result is equal to nil because none of the items of the array are greater than 10.

let result = numbers.first { $0 > 10 }

Finding an Object in an Array

Even though the previous example uses an array of integers, the first(where:) method can be used for any type of array. Take a look at the following example. We create an array of User objects and use the first(where:) method to find the first user with a first name equal to Emma.

import Foundation

struct User {

    let first: String
    let last: String

}

let users = [
    User(first: "Emma", last: "Jones"),
    User(first: "Mike", last: "Thompson"),
    User(first: "Lucy", last: "Johnson"),
    User(first: "James", last: "Wood"),
    User(first: "Cathy", last: "Miller")
]

let result = users.first { $0.first == "Emma" } // Emma Jones

Filtering Objects of an Array

The first(where:) method returns a single object, or nil if no object was found. If you need every object that matches a condition, then the filter(_:) method is what you need. In the following example, we filter the array of users to only include users whose last name starts with the letter J. Two users match that condition.

import Foundation

struct User {

    let first: String
    let last: String

}

let users = [
    User(first: "Emma", last: "Jones"),
    User(first: "Mike", last: "Thompson"),
    User(first: "Lucy", last: "Johnson"),
    User(first: "James", last: "Wood"),
    User(first: "Cathy", last: "Miller")
]

let result = users.filter { $0.last.starts(with: "J") } // Emma Jones, Lucy Johnson

The return type of the filter(_:) method isn't of an optional type. It returns an array. If none of the objects of the array match the condition, the returned array is empty.

The syntax of the filter(_:) method is very similar to that of the first(where:) method. The filter(_:) method accepts a closure as its only argument. The closure accepts an object of type Int and its return value is Bool. The resulting array includes every object of the array for which the closure returns true.

Swift Performance

Like many other programming languages, Swift includes a wide range of optimizations to improve performance. Let's revisit the second example.

import Foundation

struct User {

    let first: String
    let last: String

}

let users = [
    User(first: "Emma", last: "Jones"),
    User(first: "Mike", last: "Thompson"),
    User(first: "Lucy", last: "Johnson"),
    User(first: "James", last: "Wood"),
    User(first: "Cathy", last: "Miller")
]

let result = users.first { $0.first == "Emma" } // Emma Jones

The closure that is passed to the first(where:) method is executed once. Why is that? The closure that is passed to the first(where:) method returns true for the first User object of the array. Because the first(where:) method returns a single object and it found a match, it makes no sense to execute the closure for the remaining objects of the array. This is subtle but important detail.

Last Where

Swift also has a convenient method for finding the last element of an array that matches a given condition. The syntax is identical to that of the first(where:) method. The difference is, as you may have guessed, that the last element of the array for which the closure return true is returned. Take a look at this example.

import Foundation

struct User {

    let first: String
    let last: String

}

let users = [
    User(first: "Emma", last: "Jones"),
    User(first: "Mike", last: "Thompson"),
    User(first: "Lucy", last: "Johnson"),
    User(first: "James", last: "Wood"),
    User(first: "Cathy", last: "Miller")
]

let result = users.last { $0.first == "Lucy" } // Lucy Johnson

Be Careful

Always be careful when using first(where:) and last(where:). The return types of these methods are of an optional type. These methods return nil if none of the elements of the array match the given condition.

Find Object in Array