Ready-made methods
Finding the largest value or adding numbers up are jobs you can write with a loop, and they come up all the time. Swift arrays have methods for them:
let prices = [4, 9, 2, 7]
print(prices.min()!)
print(prices.max()!)
print(prices.reduce(0, +))
2
9
22
arr.min()gives the smallest value of the array.arr.max()gives the largest value.arr.reduce(0, +)adds all the values.
Why min() and max() need a !
min() and max() return an optional. An empty array has no smallest value, so for it they give nil. The ! takes the value out of the optional. When you know the array has at least one item, as with the seven days in this lesson, the ! is safe.
For an empty array, arr.min()! crashes the program, because the ! finds nil. When the array may be empty, supply a fallback with ??:
let none: [Int] = []
print(none.min() as Any)
print(none.min() ?? 0)
print(none.reduce(0, +))
nil
0
0
These methods work on any array whose items can be compared. For strings, ["pear", "apple", "fig"].min()! is "apple" and the max() is "pear", by alphabetical order.
Without the ! or ??, you can't use the value as a number. nums.min() + 1 is rejected with "value of optional type 'Int?' must be unwrapped to a value of type 'Int'".
reduce and the missing sum()
Swift has no sum() method. reduce(0, +) fills the gap. It starts at 0 and combines the running total with each value using +, from left to right. The result for an empty array is the start value, 0, so no ! is needed. The same total can be written with an explicit closure, reduce(0) { $0 + $1 }, which gives the same answer. In reduce(0, +), the plain operator + is passed in place of that closure.
It works for other number types too: [2.5, 1.5].reduce(0, +) is 4.0. The start value takes the type of the items.
The global max(a, b) compares two values. arr.max() looks at a whole array. For example, max(3, 8) is 8.
The lesson's exercise
minutes holds the minutes practiced on each day of a week. The task stores the shortest day in shortest, the longest in longest and the week's total in total:
let shortest = minutes.min()!takes the smallest value, 15.let longest = minutes.max()!takes the largest, 60.let total = minutes.reduce(0, +)adds the seven values: 235.
let minutes = [25, 40, 15, 60, 30, 45, 20]
let shortest = minutes.min()!
let longest = minutes.max()!
let total = minutes.reduce(0, +)
print("shortest = \(shortest)")
print("longest = \(longest)")
print("total = \(total)")
The output is:
shortest = 15
longest = 60
total = 235
Compared with a loop, the methods save the starting value and the update step. A loop that finds the largest value needs a start such as the first item, then an if inside the loop; arr.max() does all of that, and its optional result also covers the empty-array case that a loop would have to handle on its own.
Common mistakes
- Calling
sum(). It doesn't exist on arrays. Usereduce(0, +). - Forgetting the start value.
reduce(+)doesn't compile: the compiler reports a missing argument, because the first argument is the value to start from. - Leaving out the
!. The constant would then hold anInt?, and the string would printOptional(15)instead of15. - Using
!on a possibly empty array. That crashes. Use?? 0or checkisEmptyfirst.
Key takeaways
min()andmax()return optionals, which arenilfor an empty array.!is fine when the array can't be empty;??gives a fallback when it can.reduce(0, +)is Swift's sum, and it gives 0 for an empty array.
