It’s simple ! Let’s start with :
.map :
The whole idea is about mapping collection elements , do something on them and then return the same-sized array of them !
So, here we are creating a new array from the other array . It is done with an O(n) complexity since it iterates through the whole array .
What is O(n)? it’s just a simple measure helping us represent the algorithms complexity. Soon I’ll publish an article on What is O(n) ? Swifty way and examples to help you understand it better .
Well let’s see an example on .map in Arrays to help you understand it better :)
import Foundation
var array = [2, 3, 4, 5]
// 1. Just pass a closure as parameter in .map function and that's that !
let newArray_ModernStyle = array.map { (element) -> Int in
element * element
}
print(newArray_ModernStyle) // Result -> [4, 9, 16, 25]
//2. It's what swift offers to help us live better :) $0 acts as (element) in _ModernStyle's map closure.
let newArray_SwiftyWay = array.map { $0 * $0 }
print(newArray_SwiftyWay) // Result -> [4, 9, 16, 25]
//3. What if there weren't any .map in Swift? Oh... S*#!
var newArray_OldFashioned: [Int] = []
for element in array {
newArray_OldFashioned.append(element * element)
}
print(newArray_OldFashioned) // Result? The same as above! What did you expect?!
O…K, but what about return? To make it easier than what you can find at swift.org:
While having only one line of code inside the closure , our smart complier understands that it’s what we wanted to return! element * element is the same as return element * element for the compiler in this case .
Gotcha! Wondering WTH is $0 ?! As it is explained at swift.org:
… $0 and $1 refer to the closure’s first and second arguments… . You’ll see $1 in future examples but for now , it’s enough to know that $0 simply is the first (in our case the only) argument that .map closure offers ;)
Let’s meet .map’s brothers - or maybe sisters :) .compactMap and .flatMap !
.compactMap :
Well , as I’ve already mentioned in the first part of this series , Swift’s collection types are implemented as generic types . The same thing goes for also the collections' in-built functions :) It means that there isn’t any restrictions on using .map with Array
The interesting thing here is that they also work with Optional Types ! What would happen if the .map(ped) array result in some nil values ? Here’s where our new Savior shows up :) Ladies & Gentlemen let me introduce you the .compactMap !
import Foundation
var array = ["2", "3", "Four", "Roronoa Zoro"]
let newArray_ModernStyle = array.map { (element) -> Int? in
Int(element)
}
print(newArray_ModernStyle)
Can you guess the result? It’s an Optional array of Ints :) Why Optional? because the Downcast of String to Int might fail - Just like what’s happening here . So to make the above example more readable :
...
let newArray_ModernStyle: [Int?] = array.map { (element) -> Int? in //element is a String
Int(element)
}
print(newArray_ModernStyle) // Result -> [Optional(2), Optional(3), nil, nil]
Wait , wait … What just happened ?! Do you remember what I said about .map ? … do something on each element and then return the same-sized array of them … - Yep! .map should return the same-sized array as the inputed array . In our case some of the Downcasts fail and as we expecting the same-sized array from .map to be _throw_n , it fills the array with nil as for failed Downcasts .
I guess that you’ve already guessed … Here is where .compactMap enters!
To make what developer.apple.com says about it :
.compactMap is almost the same as .map with one difference ; .compactMap discards all of nil results so the newArray contains only valid transformed elements. And here we don’t expect the same-sized array to be returned . .compactMap returns an array containing the non-nil results of calling the given transformation with each element of this sequence !
import Foundation
var array: [String] = ["2", "3", "Four", "Roronoa Zoro"]
let newArray_ModernStyle = array.compactMap { (element) -> Int? in
Int(element)
}
print(newArray_ModernStyle) // Result -> [2, 3]
let newArray_SwiftyWay = array.compactMap { Int($0) }
print(newArray_SwiftyWay) // Result -> [2, 3]
What exactly is Optional behind the scenes ? It’s built using Enum with two cases of some value and .none . I’d soon publish an article on What is Optional in Swift ? Behind the scenes to cover the whole thing !
Here is the magic of .compactMap ! As it returns an array containing the non-nil results… it discards the nil and (if needed) unnecessary results just like Optional parts of Int? . To be more clear , it converts the Optional value since it knows that the result can’t be nil ,so using Optionals is absolutely useless . But if you declare newArray as type [Int?] in the above example it would produce an array of Optional Ints while holding nils -just like big bro, .map .
It’s good to know that the complexity of ,compactMap as developer.apple.com says is O(m +n) . Thats because it iterates through the whole array of n times and produces an array of m items .
By the way, who was that Roronoa Zoro guy in our array ? He is a legend, the greatest swordsman in the whole New World, Vice Captain of Straw Hat Pirates =))))) I deeply suggest you to go and watch One Piece .In the Anime, there is only one thing wrong about Roronoa , and that’s he always loses his way and his sense of direction is absolutely awful . This time as he got lost it seems that he were here with us ;)
Let’s learn about the last .map’s brother -or maybe sister- and wrap this up -so we can go and enjoy the Holy One Piece :)
.flatMap :
It’s simple . As of introducing .compactMap beside Swift 4.1 it got even simpler than before .
Nowadays the only thing .flatMap does is to :
“Returns an array containing the concatenated results of calling the given transformation with each element of this sequence.”
To make it readable by humans ,
“in Swift 4.1+ .flatMap is used only to help you flattening a nested collection into a single array.”
-Alireza
Well, a nested collection is like an array having another array as its elements . Like an array of arrays !
import Foundation
var array: Array<[Int]> = [[1], [2, 3, 4, 5], [6, 7]]
let newArray_ModernStyle = array.flatMap { (element) -> [Int] in
element
}
print(newArray_ModernStyle)
let newArray_SwiftyWay: [Int] = array.flatMap { $0 }
print(newArray_SwiftyWay)
Wondering about the result? It’d be -> [1, 2, 3, 4, 5, 6, 7] . The whole array in flattened by .flatMap to make our newArray .
There are two things here that could be nice to point out . The .flatMap’s closure always returns Sequence and it’s also good to know that Arrays , Sets , Dictionaries and lots of collection types conform to Sequence Protocol .
As developer.apple.com mentions , Sequence is a type that provides sequential, iterated access to its elements. Simply it’s a list of values that you can step through one at a time . Consider iterating over a Sequence has complexity of O(n) .
Before putting an end to this topic , just like .compactMap , .flatMap has complexity of O(m + n) ! The reason is simple , isn’t it? :)
Next: Part (III) : What is .filter in Swift ?
Thanks a lot for coming this far and reading the article :) As you may know it’s a series about Higher Order Functions of Swift’s Collection , so make sure to read next articles as well as previous one -if you hadn’t already- to completely master this subject . Stay Safe , Code Well & Watch One Piece =)
If you have any questions, suggestions or etc. feel free to contact me at alireza@swiftycode.com . We can also chat about One Piece beside Swift -kidding- on Twitter at @swiftycode_com .