I am porting the Linkage program to the Mac in Swift, as I’ve mentioned in other posts. I’m trying to be “Swifty” because a language should be taken advantage of and because I like to learn new programming paradigms (or simply styles since this might not be a paradigm shift). I was about to write some code that looked like this:

var anyLinkSelected = false
for link in connector.links {
if link.isSelected {
anyLinkSelected = true
}
}

I figured there must be a better way to do this. Since I’m working with an array, I wondered if there was a function like map() or filter() that would do this. Well, there is one other interesting array function called “reduce” that seemed interesting. The documentation says that the reduce() function reduces the contents of an array down to a single value. I looked up the definition of reduce() and lo and behold, the reduce function accepts an initial value for the result and it can be any type! That means I can write my code like this:

let anyLinkSelected = connector.links.reduce( false ) { $0 || $1.isSelected }

For non-Swifty programmers, I’ll explain; The reduce() function accepts two parameters with the second one being a function. Since that second parameter is also the last parameter, Swift lets us specify that function after the closing parenthesis. The function that gets passed in will have two unnamed parameters that can be accessed with $0 and $1 (something I maybe wish they would have changed so we could just use names instead). The $0 parameter is the current value of a variable that is of the type passed in as the first parameter to reduce(). The $1 is the current element of the array. So this function, the second parameter to reduce(), is called for every element of the array. My new code will end up returning a boolean value that is true if the isSelected value of one of the links is true.

I should mention that Swift has a weird feature where a function with a single statement is assumed to return the result of that statement as the return value of the function. That’s why there is no “return” in the reduce function parameter. It could just as easily have looked like this: “return $0 || $1.isSelected”

There is one downside to the reduce function and that’s that it iterates through the entire array. I see no way to stop processing as soon as one of the links is seen as selected. The C++ programer in me wants to write the most efficient code possible and checking all elements of an array is a waste of CPU cycles if any but the last element gives the desired result. Swift seems a little annoying in this regard except that I can just as easily write this using the code I first showed at the top of this post. So it’s not really Swift being ba with performance, or Swift programmers not being performance oriented in their work; It’s just me wanting the best of both worlds where I can use a cool Swift feature while getting a benefit it wasn’t designed to provide. And if it isn’t obvious, calling a function for every element of the array is probably way more of a performance problem that not breaking from the array when the desired value is found.

While writing this, I discovered that there is the equivalent of a find() function that should not step through every element of the array if it find what it’s looking for before that:

let anyLinkSelected = connector.links.first( where: { $0.isSelected } )?.isSelected ?? false

This should do the trick. This will call the unnamed “where” function for every element of the array until a true result is returned from it (remembering that the “return” statement isn’t needed in single-statement functions). This is more complicate looking because the first() function returns an element of the array or nil if one is not found. because of that, there is the ? at the end of the first() function call so that the isSelected value is only retrieved if the result of first() is not nil. And if it is nil, the ?? operator tells the code to use the value following it instead. So essentially, this says to get the first matching element or nil and then use either the isSelected member unless nil had been returned, in which case use “false” in my case.

I actually like the simplicity of the reduce() function. Fewer characters in code is always a good thing. But that code is just a tiny bit less clear about what it’s trying to do.

Dave