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 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
Ready? Let’s go.
- Manually Finding an Item in an Array
- Finding an Item in an Array with “firstIndex(of:)”
- Finding an Item in an Array with “firstIndex(where:)”
- Finding All Items in an Array with “all(where:)”
- 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. Where’s the name in the array?
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
Here’s what that looks like in Swift code:
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:
- We’re initializing an array called
names
of type[String]
, which contains a few names - We’re initializing a string called
searchValue
, this is the name we’re looking for - 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 tosearchValue
. It compares the string values, and evaluates totrue
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 from0
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.
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:
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 returnstrue
orfalse
. 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 isfalse
, so the function moves on to the next item. - It’ll reach
1 < 7
, which istrue
. Because that’s a match, thefirstIndex(where:)
function returns the index of this item, which is3
.
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 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]
Awesome!
Further Reading
The Swift programming language lets us do programmatic somersaults, cartwheels and saltos – with just a few lines of code. In this tutorial, 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: