Iterating over collections with closures
In Swift, collections implement some very handy features often associated with functional programming. These features come in the shape of functions that you can apply to a collection to perform an operation on it. Operations include things like removing the first element, transforming each element, or filtering out certain elements.
All of these functions make use of closures, as you will see now. The first function allows you to filter out certain elements, like so:
var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter { return $0 > 5
}
Here, you create an array of Double to represent the prices of items in a shop. To filter out the prices which are greater than $5, you use the filter function. This function looks like so:
func filter(_ isIncluded: Element -> Bool) -> [Element]
This means that filter takes a single parameter, which is a closure (or function) that takes an Element and returns a Bool. The filter function then returns an array of Elements. In this context, Element refers to the type of items in the array. In the example above, Doubles.
The closure’s job is to return true or false depending on whether or not the value should be kept or not. The array returned from filter will contain all elements for which the closure returned true.
In your example, largePrices will contain:
(10, 8.19)
Note: The array that is returned from filter (and all of these functions) is a new array. The original is not modified at all.
However, there is more!
Imagine you’re having a sale and want to discount all items to 90% of their original price. There’s a handy function named map which can achieve this:
let salePrices = prices.map { return $0 * 0.9
}
The map function will take a closure, execute it on each item in the array and return a new array containing each result with the order maintained. In this case, salePrices will contain:
[1.35, 9, 4.491, 2.07, 7.371]
Another handy function is called reduce. This function takes a starting value and a closure. The closure takes two values: the current value and an element from the array. The closure returns the next value that should be passed into the closure as the current value parameter.
This could be used with the prices array to calculate the total, like so:
let sum = prices.reduce(0) { return $0 + $1
}
The initial value is 0. Then the closure calculates the sum of the current value plus the current iteration’s value. Thus you calculate the total of all the values in the array. In this case, sum will be:
26.98
Now that you’ve seen filter, map and reduce, hopefully it’s becoming clear how powerful these functions can be, especially thanks to the syntax of closures. In just a few lines of code, you have calculated quite complex values from the collection.
These functions can also be used with dictionaries. Imagine you represent the stock in your shop by a dictionary mapping the price to number of items at that price.
You could use that to calculate the total value of your stock like so:
let stock = [1.5:5, 10:2, 4.99:20, 2.30:5, 8.19:30]
let stockSum = stock.reduce(0) { return $0 + $1.key * Double($1.value)
}
In this case, the second parameter to the reduce function is a named tuple containing the key and value from the dictionary elements. A type conversion of the value is required to perform the calculation.
Here, the result is:
384.5
There are also a few different functions which can be helpful when you need to chop up an array. The first function is called dropFirst, which works like so:
let removeFirst = prices.dropFirst() let removeFirstTwo = prices.dropFirst(2)
The dropFirst function takes a single parameter which defaults to 1 and returns an array with the required number of elements removed from the front. In this case the results will be as follows:
removeFirst = [10, 4.99, 2.30, 8.19]
removeFirstTwo = [4.99, 2.30, 8.19]
Just like dropFirst, there also exists dropLast which removes elements from the end of the array. It works like this:
let removeLast = prices.dropLast() let removeLastTwo = prices.dropLast(2)
The results of these are as you would expect, as follows:
removeLast = [1.5, 10, 4.99, 2.30]
removeLastTwo = [1.5, 10, 4.99]
And finally, you can select just the first or last elements of an array as shown below:
let firstTwo = prices.prefix(2) let lastTwo = prices.suffix(2)
Here, prefix returns the required number of elements from the front of the array, and suffix returns the required number of elements from the back of the array.
The results of this function are:
firstTwo = [1.5, 10]
lastTwo = [2.30, 8.19]
That wraps up collection iteration with closures!