How to handle initial nil value for reduce functions

2.9k Views Asked by At

I would like to learn and use more functional programming in Swift. So, I've been trying various things in playground. I don't understand Reduce, though. The basic textbook examples work, but I can't get my head around this problem.

I have an array of strings called "toDoItems". I would like to get the longest string in this array. What is the best practice for handling the initial nil value in such cases? I think this probably happens often. I thought of writing a custom function and use it.

func optionalMax(maxSofar: Int?, newElement: Int) -> Int {
    if let definiteMaxSofar = maxSofar {
        return max(definiteMaxSofar, newElement)
    }
    return newElement
}   

// Just testing - nums is an array of Ints. Works.
var maxValueOfInts = nums.reduce(0) { optionalMax($0, $1) }   

// ERROR: cannot invoke 'reduce' with an argument list of type ‘(nil, (_,_)->_)'
var longestOfStrings = toDoItems.reduce(nil) { optionalMax(count($0), count($1)) }
3

There are 3 best solutions below

0
On BEST ANSWER

It might just be that Swift does not automatically infer the type of your initial value. Try making it clear by explicitly declaring it:

var longestOfStrings = toDoItems.reduce(nil as Int?) { optionalMax($0, count($1)) }

By the way notice that I do not count on $0 (your accumulator) since it is not a String but an optional Int Int?

Generally to avoid confusion reading the code later, I explicitly label the accumulator as a and the element coming in from the serie as x:

var longestOfStrings = toDoItems.reduce(nil as Int?) { a, x in optionalMax(a, count(x)) }

This way should be clearer than $0 and $1 in code when the accumulator or the single element are used.

Hope this helps

1
On

Initialise it with an empty string "" rather than nil. Or you could even initialise it with the first element of the array, but an empty string seems better.

1
On

Second go at this after writing some wrong code, this will return the longest string if you are happy with an empty string being returned for an empty array:

toDoItems.reduce("") { count($0) > count($1) ? $0 : $1 }

Or if you want nil, use

toDoItems.reduce(nil as String?) { count($0!) > count($1) ? $0 : $1 }

The problem is that the compiler cannot infer the types you are using for your seed and accumulator closure if you seed with nil, and you also need to get the optional type correct when using the optional string as $0.