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:
let scores = [7, 9, 4]
var total = 0
for score in scores {
total += score
}
print(total) // 20Let's go through it step by step:
- On each iteration,
for score in scoresputs the next element into the loop constantscore: first7, then9, then4. - The body adds the element to
total. - The elements run out, the loop ends, and
printprints20.
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.
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.
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:
var marks = [3, 1, 2]
marks.sorted() // ✗
print(marks) // [3, 1, 2]warning: result of call to 'sorted()' is unusedThe 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
marksis declared withvar, sort the array itself with thesort()method: aftermarks.sort(),marksholds[1, 2, 3].
Key points
for x in arraygoes through the elements in order and never goes past the bounds of the array.enumerated()gives a "number, element" tuple with numbers from0, andindicesgives all the valid indices.sorted()returns a new array, whilesort()sorts the array itself, declared withvar;reversed()does not change the array.