Deep Dive into Map Function in Swift

It iterates, applies, transforms, collects. It’s your functional best friend, map.

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.
Parameters
transform
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!

  1. 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, and Array. 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. :)