What Is the Reduce() Function Doing, in Swift

What does the reduce(_:_:) function do in Swift?

By the way, if you’re wondering what precisely reduce does, you can always refer to the source code, where you can see the actual code as well as a nice narrative description in the comments.

But the root of your question is that this code is not entirely obvious. I might suggest that if you’re finding it hard to reason about the code snippet, you can replace the opaque shorthand argument names, $0 and $1, with meaningful names, e.g.:

let verticalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in 
max(nextArray.count, previousMax)
}

let horizontalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in
max(nextArray.max() ?? 0, previousMax)
}

By using argument names that make the functional intent more clear, it often is easier to grok what the code is doing. IMHO, especially when there are multiple arguments, using explicit argument names can make it more clear.


That having been said, I’d probably not use reduce and instead do something like:

let verticalMax = pipsPerRowForRank
.lazy
.map { $0.count }
.max() ?? 0

To my eye, that makes the intent extremely clear, namely that we’re counting how many items are in each sub-array and returning the maximum count.

Likewise, for the horizontal one:

let horizontalMax = pipsPerRowForRank
.lazy
.flatMap { $0 }
.max() ?? 0

Again, I think that’s clear that we’re creating a flat array of the values, and then getting the maximum value.

And, in both cases, we’re using lazy to avoid building interim structures (in case our arrays were very large), but evaluating it as we go along. This improves memory characteristics of the routine and the resulting code is more efficient. Frankly, with an array of arrays this small, lazy isn’t needed, but I include it for your reference.


Bottom line, the goal with functional patterns is not to write code with the fewest keystrokes possible (as there are more concise renditions we could have written), but rather to write efficient code whose intent is as clear as possible with the least amount of cruft. But we should always be able to glance at the code and reason about it quickly. Sometimes if further optimization is needed, we’ll make a conscious decision to sacrifice readability for performance reasons, but that’s not needed here.

What is the reduce() function doing, in Swift

Recall that reduce works by repeated applying a function (that is the closure you pass in) to an accumulator (that is the initial), for each element of the sequence:

var acc = initial
for element in array {
acc = f(acc, element) // f is the closure
}

Now knowing that, this reduction will make a lot more sense if you give meaningful names to $0.0, $0.1 and $1. Here I have renamed $0 to (prev, total), and $1 to curr.

func countUniques<T: Comparable>(_ array: Array<T>) -> Int {
let sorted = array.sorted()
let initial: (T?,Int) = (.none, 0)

let (_, total) = sorted.reduce(initial) { acc, curr in
let (prev, total) = acc
return (curr, prev == curr ? total : total + 1)
}
return total
}

prev is used to keep track of the previous element that we have seen. Notice how we always return curr as the first element of the pair. This is to say that the current element that we are seeing, becomes the "previous element" in the next iteration. Also note that it starts off .none (nil), as we have not seen anything yet.

If prev is not curr, we add 1 to total, otherwise we use the same total. In this way, we can count how many times we see a different element from the one just saw.

For a sorted array, "how many times we see a different element from the one just saw" is the same number as the number of unique elements, because the duplicates will all be grouped next to each other.

Using the reduce() function in Swift without a second argument

What you are seeing is a trailing closure.

You write a trailing closure after the function call’s parentheses, even though the trailing closure is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the first closure as part of the function call.

(emphasis mine)

Starting from Swift 5.3, you can write multiple trailing closures for a function call. Before that only the last argument, that is also a closure, can be a trailing closure.

So the reduce call is equivalent to:

let totalIncome = peopleArray.reduce(0, {(result, next) -> Double in
return result + next.income
})

I am not sure whether this is a syntactical variation of writing the same thing (i.e. if somehow two arguments are indeed being passed into the reduce() function)

Yes, this is just a syntactical variation, or "syntactic sugar" as we'd like to call it.

How do you use reduce(into:) in swift

Inside the closure, "temp" is the result format which is [[][]] and "i" is each number. As you said it processes all numbers in a loop. When % is used it returns the division remainder, so for the odd numbers like "1,3,5", it returns "1" and for the even numbers "0", which means that "temp" appends these values to the array in these respective indexes.

So if we debug and replace the variables for constants the results would be:

temp[1].append(1) //1%2 = 1/2 left 1 [[][1]]
temp[0].append(2) //2%2 = 2/2 left 0 [[2][1]]
temp[1].append(3) //3%2 = 3/2 = 1 left 1 [[2][1,3]]
temp[0].append(4) //4%2 = 4/2 left 0 [[2,4][1,3]]
temp[1].append(5) //5%2 = 5/2 = 2 left 1 [[2,4][1,3,5]]

According to the documentation the closure is called sequentially with a mutable accumulating value initialized that when exhausted, is returned to the caller.

Is there a way to break from an array's reduce function in Swift?

As others have suggested, you can use contains for this purpose:

var flags = [false, false, true, false, false, true, false]
contains(flags, true) //--> true

Another option is to use find to search for the first instance of what you're looking for, in this case true:

var flags = [false, false, true, false, false, true, false]    
find(flags, true) // --> 2, returns nil if not found
let containsTrue = (find(flags, true) != nil)

Edit: Newer versions of Swift expose these functions on the related collection protocols instead of global functions.

var flags = [false, false, true, false, false, true, false]
flags.contains(where: { $0 == true })
flags.contains(true) // if checking for a specific element
let index = flags.firstIndex(where: ${ $0 == true }) // --> 2

iOS Swift - Reduce function

reduce is a method that's used to reduce an array into single value using the operator that you provide to construct the final result. Most demonstrations of this available in tutorials use + or * to reduce an array of numbers into a single sum or multiplication result.

The method you're using takes the input array units[s] and an initial value NSMutableSet() (an empty set), then applies the closure to each element in sequence.

Your code seems to indicate that the elements of units[s] are again arrays; so your data might look something like this:

units[s]: [
[1, 2, 3, 4],
[5, 6, 7, 8],
[1, 3, 5, 7]
]

Making ps be:

ps: [ 1, 2, 3, 4, 5, 6, 7, 8 ]

after your reduce call.

Replicating Array.reduce() method

The standard reduce function makes use of generics. See the Generics chapter in the Swift book.

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

It has two generic types: Result and Element. Element comes from the type of the values in the collection and Result comes from the result type of the reduced value.

So your first step is to use the identical signature in your own perform function.

But in doing so you will discover that you now need to make your MyArray class also based on a generic instead of being hardcoded to work only with Int.

And in attempting to do that you will discover that you can't define MyArray to be generic and support the singleton pattern at the same time. So you need to remove instance and getIntance().

The end result becomes:

public class MyArray<Element> {
private var arr: [Element] = []

public init() {}

public func insert(value val: Element) {
arr.append(val)
}

/*************** Custom reduce like function ***************/

public func perform<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) -> Result) -> Result {
var result = initialResult

for element in arr {
result = nextPartialResult(result, element)
}

return result
}
}

With this in place, your first example becomes:

var arr1 = MyArray<Int>()
arr1.insert(value: 1)
arr1.insert(value: 2)
arr1.insert(value: 4)
arr1.insert(value: 3)
arr1.insert(value: 2)
arr1.insert(value: 5)
arr1.insert(value: 2)
arr1.insert(value: 2)

// :Complex calculations left for user to implement
var result = arr1.perform(0) {
return $0 + ( $1 * $1)
}
print(result)

And this outputs the desired result of 67.

In the end, it works but if you'll notice, there's no point to this MyArray class (other than a learning exercise). Just use Array.

Swift reduce function issue

Here is some code for you, which is an aggregation of different stackoverflow answers.

class HeartRate
{
var hr = 0
var date: Date?
}

extension Collection where Iterator.Element == HeartRate, Index == Int {
/// Returns the average of all elements in the array
var average: Double {
return isEmpty ? 0 : Double(reduce(0) { $0 + $1.hr }) / Double(endIndex-startIndex)
}
}

var arrHeartRate = [HeartRate(), HeartRate()]
let minDate = Date().addingTimeInterval(-300) //get 5min before time
let rateInLast5Min = arrHeartRate.filter {
guard let date = $0.date else { return false }
return date > minDate
}
var avgHR = rateInLast5Min.average


Related Topics



Leave a reply



Submit