Create Your Own Reduce Function in Swift
By: Steven Curtis
Tags: Software Engineering, Mobile App Development, Swift, Programming, Software Development

The Motivation

Reduce is one of the coolest higher — order functions available to us in Swift.

What if we could look about how reduce works under the hood, and perhaps mangle the functionality to be…whatever we would want it to be.

The reduce function

The original

The classic, canonical use is to use reduce to perhaps sum an Array

var arr = [1,2,3,4]

arr.reduce(0, {$0 + $1}) //10

This is, of course, using lots of magic from Swift’s type inference. The longer version (for the same array arr ) does have the same result

arr.reduce(0, { sum, newVal in

return sum + newVal

}) // 10

which makes it slightly easier to read for beginners, and slightly more tricky for the more experienced (you can’t win).

There are variations on uses for the reduce function. Although Reduce works on any array it doesn’t have to return an Integer (like the example above) but can return other types.

What about returning a String to concatenate an array?

var names = ["James", "Ahemed", "Kim"]

names.reduce("", {$0 + $1})

This version of reduce just concatenates the array of Strings.

The original: using reduce to count frequencies

It might not seem amazing to perform the above functions with reduce , but reduce is more flexible than that!

The documentation gives us something to go on here. reduce(_:_:) has a declaration :

func reduce<Result>(_ initialResult: Result, _nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

Notice the generic Result here: This can be any time. This means…it can be…a value, an array, or in the case of this example: A dictionary.

We can take an array of fruits :

var fruits = ["banana", "cherry", "orange", "apple", "cherry", "orange", "apple", "banana", "cherry", "orange", "fig"]

with which we are going to return a dictionary

fruits.reduce([:] as! [String: Int], {

a, b in

var c: [String:Int] = a

c[b, default: 0] += 1

return c

})

Although there is some tricky type casting going on, we can see that we have reduced the array down to a dictionary. This results in the following dictionary

["cherry": 3, "fig": 1, "apple": 2, "banana": 2, "orange": 3]

Our own reduce function

We can set up a function to, well reduce . The idea of this is to reproduce the in-built function.

A simple reduce function

This function is called (as shown above) with the following reduce(elements: test, initialResult: “”, nextPartialResult: {$0 + $1})

A reduce function in an extension

The tests

Let’s do this properly. Before writing this function, what do we expect it to do and what results do we expect it to give?

That is, we need to write some Unit Tests.

We can initially check the existing reduce function against these tests if this didn’t work I’d be very surprised (don’t worry, it does).

These tests are not complete, they are a rather basic suite to check if the extension performs as the original reduce (for production code more tests would be required to ensure that the function really does work as intended).

so we wish to create a new reduce function (myReduce) that operates the same way, that is on an array.

This sounds like an extension to me.

The extension

To understand this you will need some experience of iterators in Swift, and remember this is going to be called myReduce to match with the tests above.

myReduce is a generic function. This takes an initialResult and a nextPartialResult function in order to create a result of the type (generic) Result .

The initial result (of type Result ) is set to the initial result (which is fine, since the function nextPartialResult has not yet been applied to any element of the input Array).

Entering a while loop with iterator.next() , which uses a while let in Swift (so only runs if iterator.next() is not nil) and uses try to run the nextPartialResult on the result and the nextElement which is then assigned to be the result .

TO test this within the playground the ReduceTests.defaultTestSuite.run() runs the (3 tests) — which all pass!