More fun scripting with Swift and Xcode: Sorting with a closure

In my last episode, I left a problem unsolved as I needed to do a bit more research and testing in a Swift Playground: how to sort an array of String arrays, using one of the Strings to determine the sort order. This may seem a bit more than plain scripting, but it’s a task which has to be done in several situations which scripters often encounter.

My problem is that I assemble the required data from a dictionary of dictionaries, into a variable declared thus:
var theHelpList = [[String]]()

I like to think this structure out using some dummy values for clarity, as:
[["First string", "another", "yet more", "last here"],
["Second string", "This is another", "still more", "last for now"],
…]

These are compiled from a dictionary which is likely to be ordered according to the first string in each, and I want to sort those four-string inner arrays according to the content of the second string in each. It is, of course, essential to keep each inner string together, and to have full Unicode support.

This is the sort of problem which is set in textbooks on algorithm design, but I really don’t want to have to develop and debug my own algorithm. Thankfully, if I can work out how to use it, Swift 3 provides an easier approach. There’s a pair of global functions sort(by:) and sorted(by:) which can handle this very neatly, and a string comparison operator > which returns a Boolean indicating whether the first String argument is higher in the sorting order than the second.

sort(by:) and sorted(by:) differ in their returns: sort(by:) sorts a mutable argument in place, and returns a Boolean result; sorted(by:) leaves the immutable argument unaltered, and returns a new variable which has been sorted. Because my variable theHelpList is mutable, I can therefore use sort(by:).

My next task is to work out what to supply as the by: argument/parameter/predicate, which is normally a closure – here a function which returns true when the first and second items supplied to it are in the correct sorting order, and false when they are in the wrong sorting order.

There are various ways of expressing this closure. When sorting a simple array of strings, you can reduce this to just sort(by: >). Because my inner arrays of strings are nested within the outer array, I need to be a little more verbose. My solution is:
func forward(_ s1: Array<String>, _ s2: Array<String>) -> Bool {
return s2[1] > s1[1]
}

although I am sure that an expert could be even more terse.

I then call that on my array of arrays as
theHelpList.sort(by: forward)

The changed action code then reads:

helphelp05

Swift’s closures are a powerful language feature which might seem too complex to use in scripting. I argue the opposite: they are the sort of language feature which a pure scripting language may lack, and demonstrate the benefits of scripting with a general-purpose language such as Swift. Feel free to post comments giving AppleScript code which would accomplish the same task as concisely and quickly.

You’ll also be disappointed that many of the otherwise useful books on Swift 3 don’t fully explain closures and sorting. The clearest account is in Apple’s free The Swift Programming Language, in iBooks.