Software, Tech & Coding simplified.

The map() Function in Swift

In Swift, the map() function can be used to apply a function for each element in a collection.

For example, let’s square an array of numbers:

let numbers = [1, 2, 3, 4, 5]
let squared = numbers.map { $0 * $0 }

print(squared)

Output:

[1, 4, 9, 16, 25]

Here is an illustration of how the above code works:

Mapping in swift

In this guide, you learn how to use the map() function with closures on different collection types in Swift.

Before learning about the map() function it is important you understand what a closure is in Swift.

Closures in Swift – A Quick Primer

In Swift, a closure is a function that has no name. It is a block of code that can be passed around, for example as an argument to a function.

Closure Syntax

The syntax of closure looks like this:

{ (params) -> (return type) in
    statements
}

Where:

  • params represents the function arguments for the closure.
  • return type is the type of the return value.
  • statements refers to any valid Swift expressions.

Calling a Closure

When you call a regular function, you call the name of the function followed by a set of parenthesis (with possible arguments):

f(arg)

Where f is a function and arg is an argument.

Calling a closure works the same way but it looks different. A closure has no name so is no way to call a closure by name. Thus you have to “use” a closure instantly as you define it.

Here is the syntax of calling a closure in general:

({ (params) -> (return type) in statements })(arg)

Here the closure is defined on the same line as it is called.

Closure Example

Let’s square a number using a closure function that takes a number as an argument and squares it:

let result = ({ (num: Double) -> Double in return num * num })(9.0)
print(result)

Output:

81

As you can see, we define the closure and call it in the making. After this line has been executed, there is no trace of that closure.

Simplifying a Closure in Swift

Usually, closures are written in a simpler form. This is because the Swift compiler can infer a lot of things about the closure without you having to specify them.

Here are the steps to reducing the above closure into a simpler form:

// 1. A "full" closure implementation
({ (num: Double) -> Double in return num * num })(9.0)

// 2. Remove the return type of the closure.
({ (num: Double) in return num * num })(9.0)

// 3. Remove the parameter type.
({ num in return num * num })(9.0)

// 4. Remove the 'return' statement.
({ num in num * num })(9.0)

// 5. Use '$0' instead of a custom parameter name.
({ $0 * $0 })(9.0)

// 6. Remove parenthesis around the closure.
{ $0 * $0 }(9.0)

When Use Closures in Swift

A closure is useful when you only need the functionality once in your code.

Commonly, a closure is passed as an argument into another function. This is useful when you do not want to define a separate function for it.

A great example is when performing an operation on a collection of values using the map() function.

Now that you understand what a closure is it is time to learn how the map() function works.

The map() Function in Swift

In Swift, you can use the built-in map() function to modify each element in a collection.

The map() function loops through the collection, applying a function for each element. The map() function always returns an array where the transformed values are.

Before using the map() function, let’s use a for loop to transform an array of numbers.

A For Loop

Let’s create an array of numbers and square each number in the array using a for loop:

let numbers = [1, 2, 3, 4, 5]

var squared: [Int] = []
for n in numbers {
    let sq = n * n
    squared.append(sq)
}

print(squared)

Output:

[1, 4, 9, 16, 25]

This approach is perfectly valid and it works as expected. However, this loop can be made significantly shorter by using the map() function.

Next, let’s use the map() function to solve the same problem.

Map with the map() Function

The map() function takes a function as an argument and applies it for each element in a collection.

For example, given a function that squares numbers, let’s use map() function to call it on each element in an array:

// A function that squares a number
func square(num: Int) -> Int {
    return num * num
}

let numbers = [1, 2, 3, 4, 5]
// Square each number in the numbers list
let squared = numbers.map(square)

print(squared)

Output:

[1, 4, 9, 16, 25]

If you have only called functions with parenthesis before, numbers.map(square) might look strange to you.

But it is easy to understand.

The map() function does not want you to call a function. Instead, it wants to know the name of the function to be called on each element.

Under the hood, the map() function operates like a for loop such that it:

  1. Picks an element from the collection.
  2. Calls the function on that element.
  3. Stores the result in another collection.
  4. Repeats 1-3 until there are no elements left after which it returns the result collection.

Now you understand how the map() function works. Next, let’s take a more conventional approach to using the map() function by calling it with closure arguments.

Map with Closures

The map() function calls a function for each element in a collection. This function can be a regular function defined separately or a closure function defined inline.

If you need the mapping functionality only once, it is better practice to declare it as a closure directly into the map() function. This prevents you from leaving unused functions in your codebase.

For example, let’s define the squaring functionality directly into the map() function in a form of a closure:

let numbers = [1, 2, 3, 4, 5]

let squared = numbers.map({ (num: Int) -> Int in
    return num * num
})

Now there is no need for a separate function that defines how to square a number. Instead, it is a closure that is defined in the map() call.

As you learned before, you can clean up the closure quite a bit. This is because you do not need to be explicit about:

  1. The parameter type.
  2. The return type.
  3. The return statement.
  4. The parameter name.

With this in mind, the above map() function call becomes significantly shorter and more concise:

let squared = numbers.map({ $0 * $0 })

Now you know how to use the map() function with closures. Let’s take a look at calling map() on different data types in Swift.

Use map() with Other Data Types

So far you have seen how to use the map() function with arrays. As it turns out, you can also call map() on other collection types in Swift. However, the result is always an array! This means for example that mapping a dictionary to another dictionary is not directly possible.

Dictionaries And the map() Function

In Swift, you can call the map() function on a dictionary. Just remember that the map() function always returns an array.

For instance, let’s convert all values in a dictionary to upper case:

let data = [
    "name": "alice",
    "address": "imaginary street",
    "college": "oxford university"
]

let upperCased = data.map { $1.uppercased() }
print(upperCased)

Output:

["IMAGINARY STREET", "OXFORD UNIVERSITY", "ALICE"]

Notice that a dictionary is an unordered collection. This means the order of the result varies.

To map a dictionary to another dictionary in Swift:

  1. Call map() function on a dictionary to create an array of tuples of key-value pairs.
  2. Convert the array of tuples to a dictionary using Dictionary(uniqueKeysWithValues:).

For example, let’s convert a dictionary to a dictionary with uppercased values.

let data = [
    "name": "alice",
    "address": "imaginary street",
    "college": "oxford university"
]

let upperCased = data.map { ($0, $1.uppercased()) }
let formattedDict = Dictionary(uniqueKeysWithValues: upperCased)

print(formattedDict)

Output:

["college": "OXFORD UNIVERSITY", "address": "IMAGINARY STREET", "name": "ALICE"]

Range And the map() Function

In Swift, a range is a collection of values between a starting value and the ending value.

In Swift, you can call map() function range to transform the range to an array of transformed values.

For example, let’s create a range of numbers from 1 to 10 and square each number:

let squaredRange = (1...10).map { $0 * $0 }
print(squaredRange)

Output:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Set And the map() Function

In Swift, a set is an unordered collection of unique values.

You can call map() on a set to transform it to an array of transformed values.

For example, let’s square a set of numbers:

let numbers: Set = [1, 2, 3, 4, 5]
let squared = numbers.map { $0 * $0 }

print(squared)

Output:

[1, 9, 16, 25, 4]

Conclusion

Today you learned how to use the map() function in Swift.

To recap, the map() function calls a function for each element in the collection. It returns a new array with the updated values.

You can call the map() function on an array, dictionary, string, or set. However, you always get an array back.

Using the map() function lets you replace the for loops with much more elegant expressions.

Thanks for reading.

Happy coding!

Further Reading

50 Swift Interview Questions

Share

Share on twitter
Share on linkedin
Share on facebook
Share on pinterest
Share on email