Warning: code ahead.
Iterating through collections is routine. Today’s post is about a small way that Swift rocks at it. It’s not a revelation but it is something you can use in your daily work right now.
Let’s say you have an array of strings that is populated at runtime. These strings need to be put into UILabels which will be laid out evenly across the screen with a font color that alternates between black and red.
One way to accomplish that would be this:
There’s nothing wrong with doing that. However, it is prone to “off by 1 errors” in the for loop’s conditional. The for loop uses the index itself for calculating the x coordinate and determining the text color of the UILabel. It also needs a reference to the string array to get each string out of it to set the UILabel’s text.
“No big deal, any coder can write a for loop” you say. True, but you might have nested loops and more complicated logic. Plus we can do a little better than that.
What’s a better way to iterate? Using the enumerate() function instead. It is defined on the SequenceType protocol via Swift 2’s protocol extensions. The CollectionType protocol, in turn, adopts SequenceType. All Swift collections adopt CollectionType. That’s why enumerate() can be used on arrays, dictionaries, and sets.
Let’s return to my example. Calling scaleValues.enumerate() returns a lazy SequenceType where the first item is the index of the item in the sequence and the second is the item itself. This comes in handy when you need both the item and its index.
Here’s the example using enumerate() this time:
The (scaleValueIndex, scaleValue) is an implicit use of Swift’s Tuple type.
Here’s what it looks like with the “Show Result” button in the Playground:
This is a small thing, but I have been using it quite often. That probably means I’m still thinking imperatively and not functionally.
In Swift 1.2 and earlier enumerate() was a top level function. Now in Swift 2 it is directly on the SequenceType protocol itself. I much prefer this to having to remember to look for it as a free function.
Interestingly enough, because enumerate() is on the SequenceType protocol this works on Dictionaries too. I’m not sure what use the index of the key/value pair are, but it’s supported.