Deep Dive into Map Function in Swift
Deep Dive into Map Function in Swift
It iterates, applies, transforms, collects. It’s your functional best friend, map.
map(_:)
From Apple -
Returns an array containing the results of mapping the given closure over the sequence’s elements.
Parameterstransform
A mapping closure. transform
accepts an element of this sequence as its parameter and returns a transformed value of the same or of a different type.
Return Value
An array containing the transformed elements of this sequence.
The
map(_:)
function loops over every item in a collection, and applies an
operation to each element in the collection. It returns a
collection of resulting items, to which the operation was
applied.
In this example,
map
is used
first to convert the names in the array to lowercase strings and
then to count their characters.
another example is used to reduce the array element into half.
Complexity: O(n), where n is the length of the sequence.
Implementing Map
Now, let’s suppose you want to write a map function. Where would you start?
From above examples, The method takes the input which is a function and the return is an array.
More precisely, the input is a function which transforms its input (a value in the array) to another value. Note that the transformed value may be a different type than that of the input value. i.e. 𝑓: X → Y. The output, therefore, is an array containing elements of the codomain Y.
Now, before looking at the code below, try to write the function signature on your own.
func myMap<T>(_ transform: (Element) -> T) -> [T]
Congratulations! You just crossed the biggest hurdle.
Now let’s give some thought about the implementation of map. How do we actually get from [X] to [Y] and how do we use transform? We just create a new array of generic type T and fill it with the transformed values. Try to write the code for that.
You could imagine how to write your own
map
:
- take an array and return another array
- need a closure parameter to perform the transformation step
-
use a trusty
for
loop and build up an output array
Here’s what this might look like:
map
in Swift is
defined on the
Collection
protocol but I’ve defined it here as an extension of
Sequence
for
simplicity.
Since this is extending
Sequence
,
self
refers to
the input sequence
instance and
Element
is the
type of thing the array holds.
We need to add a generic type parameter
T
for the
output type. That means the transformation closure takes an
Element
and
returns a T
.
Time to submit that job application to join the Swift core team? Let’s compare and see how we did. 🤔
Standard library map
As usual, all the code will be right here inline, but you can check out the current source in Collection.swift on GitHub.
Looks pretty good!
-
In our implementation, we set up an empty mutable array. Here,
the standard library sets up a
ContiguousArray
directly.
There are three flavors of array:ContiguousArray
,ArraySlice
, andArray
. The differences are beyond the scope of this article but a contiguous array is like the textbook canonical array: a block of contiguous memory that you can index into.
Mutable arrays get initialized with some amount of storage and
automatically grow as needed. That’s useful when you don’t know
up front how many elements to hold, but that’s not the case
here. Growing the array has some performance cost, and we can be
more efficient by specifying exactly how much space we need with
reserveCapacity()
.
2. If the collection is empty, we can return early. Seems like a reasonable optimization that saves allocating a new mutable array and iterating over nothing.
How did we do overall? I think pretty well — the big complication left out of our simple implementation was dealing with the array’s count vs directly implementing Sequence and unable to add optimisation to it.
That’s an important reminder that everyday things like arrays are made up of smaller pieces: sequence, collection, subscripting behavior, etc. Those pieces need to be generic, and you get more specific as you get closer to a concrete type. Building things on the generic level means thinking outside the “how would I build this?” box, into the “how could this be built?” box.
Think & Try
Imagine compactMap functionality is not provided by swift framework, How will you write your own compactMap which will work similar to the given framework one?
Hope this article is useful for people looking to understand the higher order level function and its internal, Please ❤️ to recommend this post to others 😊. Let me know your feedback. :)