How To: Find An Item In An Array In Swift

Written by Reinder de Vries on November 1 2018 in App Development

How To: Find An Item In An Array In Swift

How do you find an item in an array in Swift? Let’s find out! In this article you’ll learn how to use the various generic functions to find matching items in an array.

We’ll get into:

  • The algorithm that’s used for finding items in an array
  • How you can find the first and last indices of matching array items
  • How to find items if you don’t exactly know what you’re looking for, with functions like first(where:)
  • How to find all items in an array that match a given predicate

Ready? Let’s go.

  1. Manually Finding An Item In An Array
  2. Finding An Item In An Array With “firstIndex(of:)”
  3. Finding An Item In An Array With “firstIndex(where:)”
  4. Finding All Items In An Array With “all(where:)”
  5. Further Reading

Manually Finding An Item In An Array

Before we dive into Swift’s ways you can find items in an array, let’s find out how such an algorithm actually works.

This will definitely help your understanding of algorithms and iOS development in general. When you’re learning iOS development, it’s a great idea to set aside some time to learn how commonly used algorithms work.

The challenge is simple: given an array of an arbitrary number of items, find the index of a given value. So, if we have an array of names, your job is to find the index for a particular name.

Here’s our approach at a glance:

  • Loop over every value in the array, from start to end
  • Keep track of the current index we’re inspecting
  • Compare the current value with the value we’re looking for
  • If it’s a match, return the current index, if it’s not, keep going

And here’s what that looks like in Swift code:

let names = ["Ford", "Arthur", "Trillian", "Zaphod", "Marvin", "Deep Thought", "Eddie", "Slartibartfast", "Humma Kuvula"]

let searchValue = "Zaphod"
var currentIndex = 0

for name in names
{
if name == searchValue {
print("Found \(name) for index \(currentIndex)")
break
}

currentIndex += 1
}

Here’s what happens at the top of the above code:

  1. We’re initializing an array called names of type [String], which contains a few names
  2. We’re initializing a string called searchValue, this is the name we’re looking for
  3. We’re initializing an integer value called currentIndex, which keeps track of the current index inside the loop

Then, we’re using a for loop to loop over every name in the names array, with for name in names { .... Inside the loop, this happens:

  • With an if statement the code checks if name is equal to searchValue. It compares the string values, and evaluates to true if they are the same.
  • When there’s a match, a line is printed out, and we’re stopping the loop with break. If we don’t do that, the loop will continue, even though we’ve already found what we’re looking for.
  • At the end of the loop, the code increases the value of currentIndex by one. This way we’re keeping track of the current array index, which goes from 0 to the end of the array.

Quite simple, right? The code just loops over every item in the array until it finds what we’re looking for.

The time complexity of the above algorithm is O(n), or linear time, because in the worst case scenario we’re looping over every n items once to find what we’re looking for. The time needed to search the array increases linearly with the number of items in the array.

We can actually save ourselves some trouble by slightly changing the for loop. And lets turn the code into a Swift function, too.

func find(value searchValue: String, in array: [String]) -> Int?
{
    for (index, value) in array.enumerated()
    {
        if value == searchValue {
            return index
        }
    }

    return nil
}

In the above function we’re using the enumerated() function to get a sequence of index-value pairs. This way we can directly get the index of a particular value, without needing to keep track of the current index ourselves.

And the function directly returns an index when it has found a value, which will stop the loop. When no value could be found, the function returns nil. As such, the return type of the function is Int?. (And note that we’re using function argument labels, too.)

You’d use the function like this:

let index = find(value: "Eddie", in: names)
print(index)
// Output: Optional(6)

Awesome! Now that we know how the algorithm works, let’s see what Swift functions we can use to find an item in an array.

Learn how to build iOS apps

Get started with iOS 12 and Swift 5

Sign up for our iOS development course Zero to App Store and learn how to build professional iOS 12 apps with Swift 5 and Xcode 10.

Finding An Item In An Array With “firstIndex(of:)”

The easiest approach to find an item in an array is with the firstIndex(of:) function. Here’s how:

if let index = names.firstIndex(of: "Marvin") {
    print(index)
}

// Output: 4

You call the firstIndex(of:) function on the array you want to search. This function is a generic, so it can be used regardless of the array’s type.

Let’s say we’re tired of Zaphod, and we want to replace him with someone else. Here’s how:

if let index = names.firstIndex(of: "Zaphod") {
    names[index] = "Prostetnic Vogon Jeltz"
}

In the above code, the index of the value "Zaphod" is first searched for in the names array. When found, that index is used to replace the value with another string. (Although I have no idea why you’d want a Vogon on your team…)

Because the firstIndex(of:) function searches an array from front to back, it’ll always return the index of the first match it finds. If you want to get the last match in an array, you can use the lastIndex(of:) function.

Finding An Item In An Array With “firstIndex(where:)”

What if you don’t know exactly what you’re looking for? That’s where the firstIndex(where:) function comes in. This function takes a closure, a so-called predicate, as a parameter to search the array for matches.

Here’s an example:

let grades = [8, 9, 10, 1, 2, 5, 3, 4, 8, 8]

if let index = grades.firstIndex(where: { $0 < 7 }) {
print("The first index < 7 = \(index)")
}

In the above code we’re working with a grades array of integers. We want to know the index of the first array item that’s below 7. We don’t know the exact number we’re looking for, only that it needs to be less than 7.

That’s what we use firstIndex(where: { $0 < 7 }) for. The code between the squiggly brackets is a closure. This closure is executed for every item of grades, and we’ve found a successful match when it returns true.

Let’s break that down:

  • The code $0 < 7 is a boolean expression that returns true or false. The $0 is shorthand for the first parameter of the closure, i.e. the value in the array that we’re inspecting.
  • The function starts at the left of the array, so it’ll evaluate 8 < 7. This is false, so the function moves on to the next item.
  • It’ll reach 1 < 7, which is true. Because that’s a match, the firstIndex(where:) function returns the index of this item, which is 3.

And we’re using optional binding to get the non-optional value from firstIndex(where:) if it doesn’t return nil.

The function firstIndex(where:) belongs to a group of functions that can select elements from an array that match a given predicate, including:

  • firstIndex(where:) returns the index of the first item matching a predicate
  • lastIndex(where:) returns the index of the last item matching a predicate
  • contains(where:) returns a boolean indicating if the array contains an element matching a predicate
  • allSatisfy(_:) returns a boolean indicating if all of the items in the array match a predicate
  • first(where:) returns the first item (not an index) of the array that matches a predicate
  • last(where:) returns the last item (not an index) of the array that matches a predicate

Keep in mind that if you want to use the above functions, the items in your array must conform to the Equatable protocol. Types that conform to this protocol can be compared with the == operator, which is used to check if two items are equal.

And your code gets pretty cool, pretty quickly from there. You can use any kind of comparison in the where closure. Things like:

  • Finding if any given string contains a character with $0.contains("Q")
  • Finding if an object’s property matches a value $0.name == "Dave"
  • Finding if a value matches a range pattern with 300 ... 599 ~= $0

Let’s move on!

The syntax for functions like firstIndex(where:) is similar to Swift’s higher-order functions map(_:), reduce(_:) and filter(_:).

Finding All Items In An Array With “all(where:)”

What if you want to find all items in an array that match a given predicate? The Swift Standard Library does not yet have a simple function for that, but we can of course make our own!

Let’s have a look. Here’s the all(where:) function:

extension Array where Element: Equatable {
    func all(where predicate: (Element) -> Bool) -> [Element]  {
        return self.compactMap { predicate($0) ? $0 : nil }
    }
}

The all(where:) function is an extension for the Array type, with a generic type constraint that says that this extension only applies for array elements that conform to the Equatable protocol. In other words, you can only use all(where:) for array items that can be compared with the == operator – just like the other firstIndex(where:) functions.

The function declaration defines a predicate parameter of closure type (Element) -> Bool, and it returns an array of type [Element]. This is exactly the same as the other functions.

Inside the function, we’re using a function called compactMap(_:). This function essentially calls a closure on every item in an array (i.e., “map”) and also removes a value when it is nil (i.e., “filter”).

The expression predicate($0) ? $0 : nil will return the value $0 when the predicate closure returns true, and nil when it returns false. Differently said, the closure will only let values pass for which predicate($0) is true, i.e. only array items that match the given condition are returned. And that’s exactly what we want!

Here’s how you use it:

let grades = [8, 9, 10, 1, 2, 5, 3, 4, 8, 8]
let goodGrades = grades.all(where: { $0 > 7 })
print(goodGrades)
// Output: [8, 9, 10, 8, 8]

Awesome!

Learn how to build iOS apps

Get started with iOS 12 and Swift 5

Sign up for our iOS development course Zero to App Store and learn how to build professional iOS 12 apps with Swift 5 and Xcode 10.

Further Reading

The Swift programming language lets us do programmatic somersaults, cartwheels and saltos – with just a few lines of code. In this article, we’ve begun to understand how these algorithms work, and then applied them to find different bits of data in arrays. Great work!

Want to learn more? Check out these resources:


Reinder de Vries

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.