Beyond Scripting in Swift: Sets, Lies, and Videotape

Sometimes you can see a tantalisingly simple solution to what could easily become a messy programming problem. This is a short account of promise, disappointment, and satisfaction. But I lied about the lies and videotape.

When I was coding the first versions of The Time Machine Mechanic, T2M2, I did not think about users with multiple backup destinations, nor anyone running manual backups. It was a blind spot of my making, which was quickly revealed by one of the beta-testers here, who pointed out the mess that those made of my neat backup analysis.

The solution to dealing with manual backups was inevitably a bit messy, but I thought that there might be an elegant solution for those with multiple destinations.

In the first couple of beta releases, I assumed that all the backups would be made to a single destination, and each time T2M2’s parser code found a log entry detailing that destination, it simply saved that string. When it came to generate its text report, it regurgitated the last string found, and left it at that.

What I didn’t want to do was to build an array of those strings, and then programmatically eliminate the duplicates. Although hardly a tough programming challenge, I’d have to trudge through partial string comparisons, which was not going to be clean or joyous.

Then I came across a really neat Swift solution: turn the array into a set, which eliminates all duplicates, and then back to an array. It’s not an efficient or quick solution, but at most there will be 24 strings in the array, so I felt its simplicity and elegance outweighed its penalties.

In code, this would be
myArray = Array(Set(myArray))
which is so short as to rival APL for conciseness.

So it would have been in Swift 3.0. But Swift 3.1 won’t let you do that. Xcode kept insisting that it needed a hashable type to be able to transform an Array into a Set, and despite my trying to fudge my way through with
myArray = Array(Set<String>(myArray))
the Swift 3.1 parser wouldn’t let me cheat it.

I was just starting to search for how to deal with the hashable type problem when another thought occurred: why build the Array in the first place? Why not go straight to a Set?

I opened a Swift playground and tried it out in code. It worked perfectly, and no one said anything rude about hashable types.

So now the T2M2 source does this:
var BUdisksSet = Set<String>()
declares the variable in the first place, then
BUdisksSet = Set<String>()
ensures the set is empty to begin with.

When the parser section finds the appropriate log message, it simply inserts that into the Set:
BUdisksSet.insert(theString)

Then to generate the output, it turns that Set into an Array, joins that into a continuous string, and adds that to the output string:
if (BUdisksSet.count > 0) {
theOutStr += (Array(BUdisksSet).joined(separator: "\n")) + "\n"
} else {
if (BUassessment < 3) { BUassessment = 3 }
theOutStr += "No backup folder is selected.\n"
}

That’s much more like it: simplicity and elegance. But please don’t try this at home with hundreds or thousands of potential array elements.