Programming in Swift: Functions & Types

Jan 4 2022 · Swift 5.5, iOS 15, Xcode 13

Part 2: Closures

14. forEach & map

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 13. Challenge: Closure Syntax Next episode: 15. compactMap & flatMap

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 14. forEach & map

Update Notes: This course was originally recorded in 2019. It has been reviewed and all content and materials updated as of October 2021.

Transcript: 14. forEach & map

Now that you know a bit about closures, you’re ready to see how they can be particularly useful when dealing with collections. Collections in Swift have several handy methods that take a closure as an argument. These closures are executed for each element in the collection, much like the body of a for loop!

Two common methods like this are forEach and map. forEach and a for loop are nearly interchangeable. They both iterate over a collection, pass each element into a block of code, and then run that code.

map works nearly the same, with one important difference. forEach doesn’t return anything, but map returns a collection. Each iteration returns an element that is added to the final, returned collection.

map is a great way to apply a some change to a collection of data, or transform a collection of one type into another type.

For example, if you followed the Your Second iOS & SwiftUI App course, then you used map to turn an array of Strings into an array of Tasks!

init(priority: Task.Priority, names: [String]) {
  self.init(
    priority: priority,
    tasks: names.map 💛{ Task(name: $0) }💛
  )
}

Let’s try out forEach and map in a playground.

For most of these methods, we’ll try to get the results from a for loop, first. To start out with a simple example, we’ll just print out all of the elements of this prices array.

for price in prices {
  print(price)
}

To do the same with forEach, start with the collection you want to iterate over. In this case, that’s the prices array.

prices

Then call the forEach method on that collection!

prices.forEach(

You can see this method takes one closure (or function) as an argument. That closure will take the same type as the elements in the collection, and return nothing.

In this case, prices is an array of Doubles, so the closure takes a Double. Hit return and let the closure auto-complete:

prices.forEach { (<#Double#>) in
  <#code#>
}

Call the parameter price, and then in the body just print it out.

prices.forEach { (😺price🛑) in
  😺print(price)🛑
}

Now go ahead and run the playground to see you get the same results in the console from the for loop and forEach. This closure is already pretty short, but, for practice, let’s write this closure in the shortest form we can.

There’s already no parameter or return type, so the only thing left to do is remove the parameter name and in

prices.forEach { ❌(price) in❌

and replace the use of that parameter name with a $0.

prices.forEach { print(😺$0🛑) }

Run the playground one more time, just to make sure, and there you go! Same results. So that’s forEach. What about map?

Let’s say we want to have a 10% off sale on whatever all of these prices are for! To get those values, we’d want to multiply each element in the array by .9 to get 90% of the price.

You could do that with a for loop, as well. It would look something like this. Create an empty array.

var arrayForSalePrices: [Double] = []

Loop through all of the prices,

for price in prices {

}

multiply each one by .9, and add the result to the array.

  arrayForSalePrices.append(price * 0.9)

Then check the value of arrayForSalePrices

arrayForSalePrices

There’s the prices with 10% chopped off! The map function can help us do exactly the same thing, but with less code!

map takes a closure, executes it on each element in a collection, and returns a new array containing each result. So create a new constant to store those sale prices in…

let salePrices = 

and then call map on the prices array

let salePrices = 😺prices.map { (<#Double#>) -> T in
  <#code#>
}

For map, the closure you pass in takes one parameter. In this case, it takes a Double! Just like with forEach, the parameter type is decided by the collection you’re calling map on. That means we can just give it a name, and rely on type inference for the type. Let’s call it price.

let salePrices = prices.map { (😺price❌) -> T in

This “T” stands for type, and in our case, it will also be a Double.

let salePrices = prices.map { (price) -> 😺Double❌ in

The return type can be the same as the parameter type, but it doesn’t have to be! We’ll look at an example of returning a different type in a minute.

Inside the closure, return the same calculation as the for loop, “price times 0.9”.

let salePrices = prices.map { price -> Double in
  price * 0.9
}

Notice that map maintains the order, just like the for loop. We get exactly the same result.

We can also shorten this closure up! The return type can be inferred from the contents of the closure, and we don’t have to name this parameter. So the entire closure could become “$0 times 0.9”

let salePrices = prices.map { 😺$0 * 0.9🛑 }

Now, maybe we need to get all of these sale prices, nicely formatted, into some labels.

let priceLabels

We can use map, again, to transform this Double array into a String array!

let priceLabels = salePrices.map {...

We’ll write this one in the longer form. Call the parameter price and have it return a String

 (price) -> String in

and then try out a String initializer that will let us round all of the prices down to two decimal places.

String(format: "%.2f", price)

Run the playground and on the side you’ll see your sale prices, rounded off to two decimal places, in String form!