How To: Map, Reduce and Filter in Swift

Written by: Reinder de Vries, May 3 2017, in App Development

Map, Reduce and Filter in Swift

The Swift programming functions Map, Reduce and Filter can be challenging to wrap your head around. Especially, if you’ve always written for-loops to solve iteration problems!

The Map, Reduce and Filter functions come from the realm of functional programming. In Swift, you can use Map, Reduce and Filter to loop over collection types like Array and Dictionary without using a for-loop.

When building apps you typically use the procedural or object-oriented way of programming. Functional programming is different: it only deals with functions. No variables, no “states”, no for-loops – just functions.

The Swift programming language lends itself perfectly for functional programming. You don’t need to strictly write functional code though, simply adopting concepts from functional programming (like Map, Reduce and Filter) can help you learn how to code better.

Map, Reduce and Filter are often called higher-order functions, because these functions take a function (a closure) as input and return functions as output. Strictly speaking, Swift will return the results of an operation (i.e. a mapped array) when using higher-order functions, whereas a “pure” functional language will return a collection of functions.

In case you’re thinking: “Dude, I don’t need functional programming or data processing, because my apps don’t do that!” – don’t stop here.

In recent app projects I’ve used Map, Reduce and Filter on multiple occasions:

  • Filtering cost/revenue values, to meet a certain threshold, before including them in a line graph
  • Reducing thousands of movie ratings into one average rating
  • Mapping a few operations (lowercase, remove “#”) on hashtags in this Photo App Template

You could of course have solved all these problems with a for-loop, but you’ll see that using map, reduce and filter functions results in more concise, readable and performant code.

In this guide you’ll learn how to use the Map, Reduce and Filter functions. You’ll perform these functions on collection types, like Array.

Here’s a brief overview:

  • The map function loops over every item in a collection, and applies an operation to each element in the collection
  • The reduce function loops over every item in a collection, and combines them into one value
  • The filter function loops over every item in a collection, and returns a collection containing only items that satisfy an include condition

Said differently:

  • The map function applies a function to every item in a collection. Think of “mapping” one set of values into another set of values.
  • The reduce function turns a collection into one value. Think of it as literally reducing multiple values to one value.
  • The filter function simply returns an array of values that passed an if-statement condition, and only if that condition resulted in true.

Fun fact: MapReduce is a fundamental Big Data processing concept, in which an intensive operation is applied in parallel to a collection. An example would be summarizing the page of a book into one word (mapping) and then storing those words in alphabetical boxes (reducing).

Skip ahead to the relevant chapters:

Ready? Let’s go!

Got a killer app idea?

Grab the App Toolbox 2017 to learn how to save time building your app, and how to 10x your app installs in the App Store. With the toolbox, you’ll immediately know how to move forward to build better, more profitable apps.

Get The App Toolbox

Using the Map Function

The map function loops over every item in a collection, and applies an operation to each element in the collection. It returns an array of resulting items, to which the operation was applied.

Let’s look at an example. Say you have an array of temperatures in Celcius that you want transformed to Fahrenheit.

You could use a for-loop:

let celcius = [-5.0, 10.0, 21.0, 33.0, 50.0]
var fahrenheit:[Double] = []

for value in celcius {
    fahrenheit += [value * (9/5) + 32]
}

print(fahrenheit)
// Output: [23.0, 50.0, 69.8, 91.4, 122.0]

Although the code works fine, it isn’t the most efficient. You need a mutable “helper” variable fahrenheit to store the calculated conversions as you work through them, and you need 3 lines of code for the conversion itself.

Check out the code sample, using the map function:

let celcius = [-5.0, 10.0, 21.0, 33.0, 50.0]
let fahrenheit = celcius.map { $0 * (9/5) + 32 }
print(fahrenheit)
// Output: [23.0, 50.0, 69.8, 91.4, 122.0]

What happens here?

  • First, an constant celcius is defined, of type Array of Double, and initialized with a few random Celcius values.
  • Second, the function map is called on the constant celcius. The function has one argument, a closure, which converts from Celcius to Fahrenheit.
  • Finally, the result is printed out.

Let’s take a closer look at the closure. If you’ve worked with closures before, you’ll recognize the short-hand closure syntax. This is a shorter way to code a closure, and leaves out much of the syntax of the closure.

You basically only work with input, as $0, and squiggly brackets { }. The $0 is the first input parameter for the closure, and the { and } signify the beginning and the end of the closure. If the closure had multiple parameters, the second parameter would be $1, the third $2, etcetera.

This is what the “expanded” code sample looks like:

let celcius = [-5.0, 10.0, 21.0, 33.0, 50.0]

let fahrenheit = celcius.map({ (value: Double) -> Double in
    return value * (9/5) + 32
})

print(fahrenheit)

The actual map function call, and its closure, is this:

... = celcius.map({ (value: Double) -> Double in
    return value * (9/5) + 32
})

As you can see, the map function is called on the array celcius. This map function takes in one argument: the closure.

The first part of the closure, starting with {, indicates that this closure has one parameter value of type Double, and should return a value of type Double. The closure body, starting with return, simply returns the result of the Celcius to Fahrenheit conversion.

If you compare the short-hand closure syntax to the expanded code above, you’ll see that:

  • The function parentheses ( and ) are omitted, because you can leave those out when the last parameter of a function call is a closure.
  • The () -> in part can be omitted, because Swift can infer that you’re using one Double parameter as input, and are expected to return a Double. Now that you’ve left out the value variable, you can use the short-hand $0.
  • The return statement can be left out too, because this closure is expected to return the result of an expression anyway.

Even though the above code sample use Double types, you’re not limited to these types. The resulting type of a map function can have a different type than what you put into it, and you can use map on a Dictionary as well.

Here’s a more complex example, from the Photo App Template:

...
regex.matches(in: self, options: [], range: NSRange(location: 0, length: self.characters.count)).map {
    string.substring(with: $0.range).replacingOccurrences(of: "#", with: "").lowercased()
}
...

In this example, the results of a regular expression are mapped, removing the # character, and converting the string to lowercase.

That’s the map function for ya!

Map, Reduce and Filter in Swift: https://learnappmaking.com/map-reduce-filter-swift-programming/Click To Tweet

Using the Reduce Function

The reduce function loops over every item in a collection, and combines them into one value. Think of it as literally reducing multiple values to one value.

The Reduce function is perhaps the hardest of Map, Reduce, Filter to comprehend. How can you go from a collection of values, to one value?

A few examples:

  • Creating a sum of multiple values, i.e. 3 + 4 + 5 = 12
  • Concatenating a collection of strings, i.e. ["Zaphod", "Trillian", "Ford"] = "Zaphod, Trillian, Ford"
  • Averaging a set of values, i.e. (7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667

In data processing, you can imagine plenty scenarios when simple operations like these come in handy. Like before, you can solve any of these problems with a for-loop, but reduce is simply shorter and faster.

This is how you use reduce:

let values = [3, 4, 5]
let sum = values.reduce(0, +)
print(sum)
// Output: 12

The function reduce takes two arguments, an initial value and a closure. As you can see in the example above, you can replace this closure by a simple expression like +, - or *.

Likewise, you can also provide your own closure, with the short-hand closure syntax, like this:

let values = [7.0, 3.0, 10.0]
let avg:Double = values.reduce(0.0) { $0 + ($1 / Double(values.count)) }
print(avg)
// Output: 6.667

In the example above, you’re calculating the average of three values 7, 3, 10. Because all the types need to match, the values array, the avg constant and the values.count are of type Double.

It’s important to note here that the closure you use for the reduce function has two parameters. The first one is the result of the previous reduction, i.e. the output of the function, denoted with $0. The second one is the current item that’s being reduced, i.e. the input for the current iteration, denoted with $1.

This input of the closure becomes clearer if you “expand” the previous example, like this:

let values = [7.0, 3.0, 10.0]
let avg = values.reduce(0.0, { (result:Double, item:Double) -> Double in 
    return result + (item / Double(values.count))
})
print(avg)
// Output: 6.667

In the example above you can see two closure parameters, result and item, both of type Double. This also makes clear why the reduce function needs an initial value – that’s the result for the first iteration of the loop!

Reduction can be challenging to grasp. You’re performing an operation on two operands, like A + B, but how can that have just one result? And of all the items in the collection, which items are used as operands?

Check this out:

3 + 4 + 5 + 6
((3 + 4) + 5) + 6
3 + 4 = 7
7 + 5 = 12
12 + 6 = 18

In the above example you’re using the + operator to add a list of numbers. On the second line you can see the order of execution, i.e. 3 + 4 is executed first. On the following lines you can see that the result of 3 + 4 is added to 5, the third item, resulting in 12, which is used as the left-hand operand of 12 + 6, resulting in 18.

In Swift code, it’s this:

let values = [3, 4, 5, 6]
let sum = values.reduce(0, +)
print(sum)
// Output: 18

In the example above, the initial value 0 is needed for the first iteration: 0 + 3 = 3.

Using the Filter Function

The filter function loops over every item in a collection, and returns a collection containing only items that satisfy an include condition. It’s like applying an if-statement to a collection, and only getting the ones that pass the condition back.

Check out this example:

let values = [11, 13, 14, 17, 21, 33, 22]
let even = values.filter { $0 % 2 == 0 }
print(even)
// Output: [14, 22]

In the above example, you’re selecting numbers from values that are even.

The expression $0 % 2 == 0 uses the remainder operator % to figure out whether $0 is divisible 2. If the remainder of the operation is 0, the number must be even.

5 % 2 == 0
// Output: false

16 % 2 == 0
// Output: true

5 / 2 leaves 1 as a remainder, and 16 / 2 leaves 0 as remainder, so therefore 16 must be an even number.

You can also see that the expressions above return a Bool, so true or false. You can assume that the closure for filter needs to return a Bool, and then only returns an array of values that passed the condition, i.e. returned true.

This is the previous example, “expanded”, and only including odd numbers:

let values = [11, 13, 14, 17, 21, 33, 22]
let even = values.filter({ (value:Int) -> Bool in
    return value % 2 != 0
})

print(even)
// Output: [11, 13, 17, 21, 33]

In the example, the closure should return a boolean value, indicated with -> Bool. It’s provided one parameter, the item in the collection, and returns the result of value % 2 != 0.

Chaining Map, Reduce and Filter

Can you combine the Map, Reduce and Filter functions? Sure you can!

Let’s say we have a class of students, and of each student you know their birth year. You want to sum the ages of all students born in or after the year 2000.

This is how you do that:

let years = [1989, 1992, 2003, 1970, 2014, 2001, 2015, 1990, 2000, 1999]
let sum = years.filter({ $0 >= 2000 }).map({ 2017 - $0 }).reduce(0, +)
print(sum)
// Output: 52

The above code sample uses chaining, in which you chain functions, and use the result of one function as input for another.

In other words, map function is called on the result array of the filter function, and the reduce function is called on the result array of the map function.

The code itself is pretty simple:

  • First, you declare an array of arbitrary birth years as integer numbers.
  • Then, you filter that array with the filter function. If the value is greater than or equal to 2000, it is included in the result array. It’s important to note here that the size of the result is smaller than the size of years, because values like 1989 aren’t included!
  • Then, you perform an operation on each of the items with the map function, calculating the student’s age in the year 2017.
  • Finally, you reduce the collection to one value with the + operation, creating a sum of all ages.
Got a killer app idea?

Grab the App Toolbox 2017 to learn how to save time building your app, and how to 10x your app installs in the App Store. With the toolbox, you’ll immediately know how to move forward to build better, more profitable apps.

Get The App Toolbox

Conclusion

Now, what if you were to code this all with for-loops? You’d need at least 3 for-loops, one if-statement, and 3 sets of helper collection variables. With Map, Reduce and Filter you’ve done it in 2 lines of code, without losing out on readability and performance. Awesome!

The Map, Reduce and Filter functions have equally powerful implementations in many programming languages, including Java, JavaScript, Ruby and PHP. Learning how to process data with functional programming concepts can be challenging, but it’ll definitely save you time and make your code more concise, readable and performant.

Learn more? Here are the official Apple docs on Map, Reduce and Filter:

Enjoyed this article? Please share!

Map, Reduce and Filter in Swift: https://learnappmaking.com/map-reduce-filter-swift-programming/Click To Tweet

Join 11.000+ app developers and marketers
  • Get a weekly curated list of app development tools, articles and resources in your inbox
  • 10x your app installs with relevant App Store Optimization and app marketing strategies
  • BONUS: Grab a free copy of the App Toolbox 2017 to supercharge your next app project
Yes, Sign Me Up!

Most Popular Content

Written By: Reinder de Vries

Reinder de Vries is an indie app developer who teaches aspiring app developers and marketers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed over 50 apps for iOS, Android and the web, and his code is used by millions of users all over the globe. When Reinder isn't building apps, he enjoys strong espresso and traveling.

Got a killer app idea?

Grab the App Toolbox 2017 to learn how to save time building your app,
and how to 10x your app installs in the App Store. With the toolbox, you'll immediately know how to move forward to build better, more profitable apps.

Get The App Toolbox

Comments & Thoughts


Leave a Reply

Markdown is supported. Your email address will not be published. Required fields are marked with *

On The Blog