Back to blog

How To Find an Item in an Array in Swift


Aasif Khan
By Aasif Khan | Last Updated on March 2nd, 2024 11:42 am | 4-min read

How do you find an item in an array in Swift? Let’s find out! In this mobile application development tutorial 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

Describe your app idea
and AI will build your App

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. Where’s the name in the array?

Manually finding an item in an array is one of the most basic operations in programming. It’s a fundamental skill that every developer should be familiar with. While Swift provides built-in methods to achieve this, understanding the manual process is crucial for a deeper comprehension of how algorithms work.

When manually searching for an item in an array:

  1. Start at the beginning of the array.
  2. Examine each item in the array one by one.
  3. If the current item matches the item you’re looking for, you’ve found it! Note down the index.
  4. If you reach the end of the array without finding the item, it means the item isn’t present in the array.

This method is straightforward and works for any type of array. However, it can be inefficient for large arrays since, in the worst case, you might have to inspect every single item in the array.

Here’s our approach at a glance:

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

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
}

This is 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. This is about as good as it’s going to get for a collection that doesn’t use a hashmap. If you need a test membership in O(1), check out Set.

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.

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, unless you like bureaucracy, highways and/or poetry.)

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.

The firstIndex(of:) function is a powerful tool in Swift that allows developers to quickly locate the position of a specific item in an array. This function is particularly useful when you have a large dataset and need to pinpoint the location of a single item without manually iterating through each element.

Some key points to remember about firstIndex(of:):

  1. It returns the index of the first occurrence of the specified value.
  2. If the value is not found, it returns nil.
  3. The function is case-sensitive, meaning “apple” and “Apple” would be treated as two different values.
  4. It’s efficient for arrays with unique values, but if an array has duplicate values, it will only return the index of the first occurrence.

For those who are new to Swift, the if let construct used in the examples is a way to handle optionals. In Swift, an optional is a type that can hold either a value or nil. The if let construct allows you to safely unwrap the optional and use its value if it exists.

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 firstIndex(where:) function is an invaluable tool when you need to locate an item in an array based on a specific condition, rather than a known value. It provides flexibility by allowing you to define the criteria for the search.

For instance, consider a scenario where you have an array of student objects, and you want to find the first student who scored above 90. Instead of iterating through the array manually and checking each student’s score, you can use the firstIndex(where:) function to achieve this in a more concise manner.

Here’s a quick example:

struct Student {
let name: String
let score: Int
}

let students = [Student(name: “Alice”, score: 85), Student(name: “Bob”, score: 92), Student(name: “Charlie”, score: 88)]
if let highScorerIndex = students.firstIndex(where: { $0.score > 90 }) {
print(“First student scoring above 90 is: \(students[highScorerIndex].name)”)
}

In this example, the function would return the index of “Bob” as he is the first student in the array with a score above 90.

The power of firstIndex(where:) lies in its flexibility. You can define any condition within the closure, making it a versatile tool for various search requirements in arrays.

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 all(where:) 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]

The all(where:) function is a custom utility that fills a gap in Swift’s Standard Library. It’s designed to retrieve all elements in an array that satisfy a particular condition, rather than just the first or last match. This can be especially useful in scenarios where you need to filter out multiple elements based on a specific criterion.

For instance, consider an array of products in an e-commerce app. If you want to fetch all products that are on sale, the all(where:) function can be a handy tool.

Here’s a brief example:

struct Product {
let name: String
let price: Double
let onSale: Bool
}

let products = [Product(name: “Shirt”, price: 20.0, onSale: true), Product(name: “Shoes”, price: 50.0, onSale: false), Product(name: “Hat”, price: 10.0, onSale: true)]
let onSaleProducts = products.all(where: { $0.onSale })
print(onSaleProducts) // Output: [Product(name: “Shirt”, price: 20.0, onSale: true), Product(name: “Hat”, price: 10.0, onSale: true)]

In this example, the function would return an array containing the “Shirt” and “Hat” products, as they are the ones on sale.

The beauty of the all(where:) function lies in its adaptability. You can define any condition within the closure, allowing for a wide range of filtering possibilities in arrays.

Conclusion

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


Aasif Khan

Head of SEO at Appy Pie

App Builder

Most Popular Posts