Map, FlatMap, Filter, Reduce – High order functions in Swift

Uplift iOS Interview

The Guide is for YOU if
  • You are preparing for an iOS interview and want to improve your skills and knowledge and looking to level up your interview game and land your dream job.
  • You want to gain confidence and ease during iOS interviews by learning expert tips and curated strategies.
  • You want access to a comprehensive list of iOS interview QA to practice and prepare.

Higher-order functions are used a lot in functional programming. A higher-order function is a function that takes one or more functions as arguments and returns a function or a value as its result. All other functions are first-order functions.

Higher-order functions use closures to allow us to pass in functionality that can then determine how we want the method to sort, map, flatMap, filter and reduce in Swift collection types such as Array, set or Dictionary. To be more precise, Swift returns the results of an operation as a transformed array while using higher-order functions, on the other hand, a “pure functional language” will return a collection of functions.

Higher-order functions provide a useful abstraction for things like callbacks and function chaining. One of the big advantages of using higher-order functions is the composition by adding multiple higher-order functions together. This technique reduces bugs and makes the code easier to read and understand. One can generate smaller functions that only take care of a specific portion e of logic by adapting higher-order functions. One can also compose more complex functions by using different smaller functions.

Let’s create a higher order function in oder to understand the basics –

func createEmail(_ name: String) -> String {
    return "\(name)@ishtiz.com"
}

func createUserName(_ name: String) -> String {
    let number = (1...1000).randomElement()
    return "\(name)\(number!)"
}

func processName(
    action: (_ parameter: String) -> String,
    name: String
) -> String {
    return action(name)
}

// Can be simplified by typealias
// typealias Action = (_ parameter: String) -> String
// func processName(action: Action, name: String) -> String {
//    return action(name)
// }

processName(action: createEmail, name: "ishtiak")
// Output ishtiak@ishtiz.com
processName(action: createUserName, name: "ishtiak")
// Output ishtiak140

Here, the first two functions are the first-order function that simply takes a name as a parameter and returns email and user name respectively. The third function is the higher-order function which takes a function as a first parameter, a name as the second parameter and returns a string.

A higher-order function can returns a function. In order to understand, take a look on “chooseAction” function

func chooseAction(isUserName: Bool) -> (_ parameter: String) -> String {
    return isUserName ? createUserName : createEmail
}

let actionUserName = chooseAction(isUserName: true)
let actionEmail = chooseAction(isUserName: false)

actionUserName("jacob")
// Output jacob582
actionEmail("jacob")
// Output jacob@ishtiz.com

In order to understand higher-order functions more accurately, I would suggest you read closure documentation.

Common higher-order functions in Swift are described below:

Map

Loops over a collection and applies the same operation to each element in the collection.

Let’s assume we need to multiply each item by 2 and add 10 in an array called numbers and convert it to a string array. We can easily achieve it by for in loop which is old-style and creates boilerplate code. You probably need an empty array where you append calculated value when using a loop. The power of map in Swift

let numbers = [1, 2, 3, 4, 5]
let strings = numbers.map{"Number \(($0 * 2) + 10)"} 
// ["Number 12", "Number 14", "Number 16", "Number 18", "Number 20"]

Let’s have a look on another example –

// I will use this model to demonstrate other higher-order functions

struct Horse {
    let name: String
    let age: Int
    let breed: String
    let availableIn: [String]?
}

let horses = [
    Horse(name: "Leo", age: 5, breed: "Friesian", availableIn: ["Netherlands"]),
    Horse(name: "Marko", age: 9, breed: "Mustang", availableIn: ["USA"]),
    Horse(name: "Ziha", age: 6, breed: "Arabian", availableIn: nil)
]

// MAP

let myFavouriteHorses = horses.map{$0.name}
// Output ["Leo", "Marko", "Ziha"]

You can also use horses.map(\.name) using the key path described in How to use the key path as functions in Swift 5.2?

FlatMap 

It flattens a collection of collections. It is used when we have multiple arrays/collections within an array/collection that we would like to combine into a single array/collection.

// Using Map:
let regions = horses.map{$0.availableIn}
// Output [Optional(["Netherlands"]), Optional(["USA"]), nil]

// Using flatMap:
let regionsCompact = horses.flatMap{$0.availableIn}
// Output ["Netherlands", "USA"]
// Removes empty array and optional

Use flatmap to remove nil from the collection of optionals 

let regions: [String?] = ["Netherlands", "USA", nil]

let mappedRegions = regions.map{$0}
// Output [Optional(["Netherlands"]), Optional(["USA"]), nil]

let flaMappedRegions = regions.flatMap{$0}
// Output ["Netherlands", "USA"]

Filter

Loops over a collection and returns an array that contains elements that meet a condition.

let numbers = [1, 2, 3, 4, 5, 6 ,7 ,8 ,9 ,10]

// Even numbers
let evenNumbers = numbers.filter{$0%2 == 0}
// Output [2, 4, 6, 8, 10]

// Even numbers that are greater than 5
let specialEvenNumbers = numbers.filter{$0%2 == 0 && $0 > 5}
// Output[6,8,10]

// Horses that are older than five years
let youngHorses = horses.filter{$0.age > 5}.map{$0.name}
// Output ["Marko", "Ziha"]

Reduce

Combines all items in a collection to create a single value. The Power of reduce in Swift

let numbers = [1, 2, 3, 4, 5, 6 ,7 ,8 ,9 ,10]

let sum = numbers.reduce(0, {$0 + $1})
// Output 55

let sumShortFormat = numbers.reduce(0, +)
// Output 55

let averageHorseAge = horses.flatMap{$0.age}.reduce(0, +) / horses.count
// Output 6

Sort

let sortedHorseNames = horses.sorted{$0.age < $1.age}.map{$0.name}
// Output ["Leo", "Ziha", "Marko"]
// Lowest age first



✍️ Written by Ishtiak Ahmed

👉 Follow me on XLinkedIn



Get Ready to Shine: Mastering the iOS Interview




Enjoying the articles? Get the inside scoop by subscribing to my newsletter.

Get access to exclusive iOS development tips, tricks, and insights when you subscribe to my newsletter. You'll also receive links to new articles, app development ideas, and an interview preparation mini book.

If you know someone who would benefit from reading this article, please share it with them.