Leave a rating/review
Notes: 15. compactMap & flatMap
Update Notes: This course was originally recorded in 2019. It has been reviewed and all content and materials updated as of October 2021.
There are two more handy methods with map in the name: compactMap and flatMap. Both of them return an array, like map, but they also do a little more work for you behind the scenes.
compactMap helps you deal with optionals. You might use it if you have an array of Optional values, or if you want to run a collection of data through some process that would produce Optionals.
compactMap will iterate through each element in a collection, perform whatever operations your closure dictates with each element, and then it will only add resulting values to the returned array if they’re non-nil.
flatMap helps you handle multi-dimensional arrays! If you have an array of arrays, or just multiple arrays that you want to combine into one, flatMap can help!
Unlike map and compactMap, flatMap’s closure argument expects an array as input. The job of that closure is to deal with each individual array, and return an array of whatever type you want the final result to be. Behind the scenes, flatMap combines all of the arrays into one, and returns it.
Let’s try both of these out in a playground.
First, compactMap. Again, let’s write this as a for loop to help illustrate what it does! What if we wanted to covert and array of user input, that come is as Strings, to an array of Ints? We’d have to create a new variable to hold the Int array,
var arrayForValidInput: [Int] = []
loop though all of these things,
for input in userInput {
}
and then, not all Strings can be turned into Ints, so we’d need to check to see if that conversions worked with a safe unwrapping practice,
guard let input = Int(input) else {
continue
}
and then add the successful conversions to a new array.
arrayForValidInput.append(input)
}
arrayForValidInput
The only one of those strings that can be turned into an Int is 15, and that’s the only thing in our array, so we’re in good shape.
compactMap can do the same thing with much less code involved! Make a new constant for valid input…
let validInput = userInput.compactMap { input in
}
and then call compactMap on userInput
let validInput = userInput.compactMap {
}
compactMap takes one closure as an argument, so we can use trailing closure syntax again.
That closure takes one parameter, and again, the type is decided by the collection you’re calling compactMap on. In this case, it’s a String! Let’s call it input, and just leave off the return type.
let validInput = userInput.compactMap { 😺input in🛑
}
If you just wanted to pass in an array of optionals, and get out the non-nil values, you could do that!
But we want to reproduce the functionality of the for loop, so let’s try to convert that input into an Int inside of the closure.
Int(input)
Now compactMap will weed out all of the nil values for us, and return an array with just the successful conversions.
Run the playground and you should get the same results from the for loop and compactMap!
The last one we’ll try in this episode is flatMap. This array contains two arrays of Dwarf names. It’s a String array array!… or maybe it’s easier to say it like “array of String arrays”.
What if we wanted to combine these arrays into one, and we only want the Dwarves who’s names come after M in the alphabet?
let dwarvesAfterM =
flatMap can do that!
let dwarvesAfterM = arrayOfDwarfArrays.flatMap {
}
It only takes one parameter, a closure, and returns an array. That closures takes one parameter, In our case, that parameter is an array of Strings. Let’s call it dwarves…
let dwarvesAfterM = arrayOfDwarfArrays.flatMap { 😺dwarves
}
and we also want it to return an array of Strings.
let dwarvesAfterM = arrayOfDwarfArrays.flatMap { dwarves 😺-> [String] in
}
The closure can alter each element you pass in, in any way you want. Again, it doesn’t have to return the same type. And, on the easier side, if you just wanted to combine the two arrays, you could simply return the input.
dwarves
In our case, though, we’re going to use a for loop to create a new array of only the dwarves whose names start with the a letter after M in the alphabet. So, create a new empty String array
var afterM: [String] = []
loop over the dwarves, checking to see if their name comes after “M” with a where clause…
for dwarf in dwarves where dwarf > "M" {
}
add any dwarf that passes that check to the array…
afterM.append(dwarf)
and finally, return the finished array from the closure!
return afterM
Go ahead and run the playground and there’s our subset of dwarves! There is and easier way to do this part, though…
var afterM: [String] = []
for dwarf in dwarves where dwarf > "M" {
afterM.append(dwarf)
}
return afterM
We’ll try that, and more!, after a challenge.