Skip to content
Theory · Arrays · lesson 5 of 8

Looping over an array

Picture yourself walking along a row of lockers and opening them one after another, from the first to the last. In each one you do something: count the items or look for the one you need. The for-in loop can walk through an array the same way.

Here is a program that adds up all the marks:

SWIFT
let scores = [7, 9, 4]
var total = 0
for score in scores {
    total += score
}
print(total)  // 20

Let's go through it step by step:

  1. On each iteration, for score in scores puts the next element into the loop constant score: first 7, then 9, then 4.
  2. The body adds the element to total.
  3. The elements run out, the loop ends, and print prints 20.

No indices are needed here at all. The loop itself goes through the elements in order and never goes past the bounds of the array.

Here the row of lockers picture is inaccurate. Once you open a locker, you can rearrange the item in it. But score is a loop constant holding a copy of the element, and you cannot change the array through it.

The number and the element together

Sometimes you need both the element and its index: for example, to print a player's place next to their points. There are two ways to do that.

enumerated()

On each iteration, scores.enumerated() gives a tuple of two parts: the element's number and the element itself. Writing (i, score) splits the tuple into two names, like let (a, b) = … in the functions theme. What to remember:

  • the number comes first, the element second;
  • the numbers start at 0, like indices;
  • if you do not need one part, you write _ in place of its name: for (i, _) in scores.enumerated().

indices

scores.indices is all the valid indices of the array, from 0 to count - 1. A loop over them gives only the numbers, and you read the element by its index: scores[i]. By the index you can also replace an element: if the array is declared with var, scores[i] *= 2 in such a loop doubles every element.

SWIFT
for (i, score) in scores.enumerated() {
    print(i, score, terminator: "; ")  // 0 7; 1 9; 2 4;
}
print()
for i in scores.indices {
    print(i, terminator: " ")  // 0 1 2
}
print()

Interviewers like to ask this: what number does enumerated() start counting from, and in what order do the parts of the tuple come? The answer: from zero, the number first and the element second.

Order

sorted()

scores.sorted() returns a new array in which the elements are in ascending order. scores itself stays the same.

reversed()

scores.reversed() gives the elements in reverse order, but not as a new array: print(scores.reversed()) does not print [4, 9, 7]. So you go through such elements with a for-in loop. The array itself does not change here either.

SWIFT
print(scores.sorted(), scores)  // [4, 7, 9] [7, 9, 4]
for score in scores.reversed() {
    print(score, terminator: " ")  // 4 9 7
}
print()

Common mistake

Expecting sorted() to sort the array itself:

SWIFT
var marks = [3, 1, 2]
marks.sorted() // ✗
print(marks)  // [3, 1, 2]
TEXT
warning: result of call to 'sorted()' is unused

The program runs, but it prints [3, 1, 2]. sorted() built a new sorted array, and nobody kept it. That is what the compiler warns about: the result of the call is unused. remove(at:) gives no such warning: it changes the array itself, so you may leave the removed element unused.

There are two ways to fix it:

  • keep the result: let sortedMarks = marks.sorted();
  • if marks is declared with var, sort the array itself with the sort() method: after marks.sort(), marks holds [1, 2, 3].

Key points

  • for x in array goes through the elements in order and never goes past the bounds of the array.
  • enumerated() gives a "number, element" tuple with numbers from 0, and indices gives all the valid indices.
  • sorted() returns a new array, while sort() sorts the array itself, declared with var; reversed() does not change the array.