Collecting Data - Learning Swift Programming (2015)

Learning Swift Programming (2015)

2. Collecting Data

This chapter explains what Swift has available for collecting and managing data. Objective-C has NSArray and NSDictionary through Cocoa. Swift is compatible and used alongside Objective-C, so you have all those NS tools available to you. In addition, you’ll also have Swift’s native arrays and dictionaries available.

This chapter describes arrays and dictionaries and the different tools available in each. You will learn how to add and remove elements from collections. You will also learn how Swift’s strong type inference allows for quickly written and strongly typed arrays and dictionaries. You will learn when to use NSArrays and when to use Swift’s own arrays.

Using Arrays

Swift is different from Objective-C in terms of arrays. In Objective-C, you can put whatever you want in an NSArray. In Swift, you must tell Swift exactly what type of things will be going into the array. This makes for extremely predictable code. An array stores multiple values of the same type in a sequential list. You can use Swift’s powerful type inference to write arrays quickly, using shorthand notation. You can also declare them verbosely, but you won’t see that done very often.

Your First Array

You can create an array by declaring a variable to hold the array and then telling Swift exactly what is going to be in that array. Here’s how you do that:

var myFirstArray:Array<Int> = Array<Int>()

Here you declare the variable myFirstArray, and you declare the variable to be of type Array<Int>. This says that the array is an array of integers. You then set myFirstArray equal to a new array of integers by adding an empty set of parentheses at the end.


Note

Of course you can write this verbose array syntax, but so you will very rarely see arrays written this way because Swift has type inference.


A Quicker Array

Of course you know from Chapter 1, “Getting Your Feet Wet,” that Swift has powerful type inference. You don’t need to declare an array as verbosely as you did earlier. Here is a quicker way:

var quickerArray = [Int]()

Here you use Int surrounded by square brackets to mean “an array of Ints.” This syntax was originally written as Int[] before Xcode beta 3. It was subsequently changed to include the square brackets around the type. If you write it in the old-fashioned way, Xcode will provide an auto-correction for you.

You can also instantiate an array with items directly in it. When you do this, Swift can infer the type of the array so you don’t even have to declare a type. Here’s how it works:

var arrayOfInts = [1,2,3,4]

This gives you an array of Ints, and if you try to add a String to it, Swift complains because Swift is strictly typed:

dontInferMe.append("hi")
// Type 'Int' does not conform to protocol 'StringLiteralConvertible'

We haven’t talked about append yet, but you can guess that it adds an item to the array.

Working with arrays in Swift gets even more awesome because you can add any old thing into an array without Swift complaining. Believe it or not, the array will still be strictly typed:

var mixedArray = [1,"hi",3.0,Float(4)]

Here you have made an array with an Int, a String, a Double, and a Float. Notice that Swift does not complain. You can click or tap with three fingers simultaneously in XCode on the variable mixedArray, and it will tell you it is of type NSArray (see Figure 2.1). Swift is smart enough to change the type to use NSArrays, which makes sense because NSArrays can contain a mixed bag of stuff.

Image

Figure 2.1 Clicking or tapping with three fingers within XCode brings up more information.

Using AnyObject

With Swift you can make an array of AnyObjects. You will see this done frequently where something has a return value of [AnyObject]. This is a nonspecific type to represent any type of class. Objective-C does not have strictly typed arrays, so in order to interface with Cocoa APIs properly, you need some flexibility to return arrays so that they can contain a mixed bag. You will often see Cocoa APIs return [AnyObject].

What’s an Array Made Of?

It is possible to inspect the entire Swift programming language in XCode so you can see exactly how it was created. You should take advantage of this as often as possible. It will teach you how Apple likes code structured, and you will learn how to effectively use different types. To see how to do this, in this section, you’ll take a look at an array. You won’t understand most of the syntax right now, but we will come back to it later, and I promise that it will all become clear to you. In the playground, write this line of code:

var a:Array<Int>

This declares a variable that is an array of Ints. In fact, you can write any type in place of Int. This is because Array is declared using generics, which you will learn all about in Chapter 9, “Becoming Flexible with Generics.”

Next, hold down the Command key on your keyboard and click on Array<Int>. You should see something similar to what is shown in Figure 2.2, depending on your version of Xcode.

Image

Figure 2.2 The skeleton of the source for arrays.

We will come back to this code shortly and step through it. For now, just know that this is not fully functioning code. It is just a bunch of declarations that are missing their implementations. One thing you can gain from this code with your current knowledge is that arrays will have a startIndex and endIndex, which you can grab. If you were to try this out with the mixedArray from earlier, it would not work because that is an NSArray, not a Swift array. Let’s create an example using the start and end index:

var ints = [1,2,3,4,5]
ints.startIndex // prints 0
ints.endIndex // prints 5

You were able to enter the Swift source code and test something you found in there. Cool!

Differences Between NSArrays and Swift Arrays

Did you notice in the preceding section that you don’t have mutable and immutable versions of the Swift array? Mutable means that something can be changed and immutable means it cannot be changed. An immutable array cannot change once it is created. Well, you really do have mutable and immutable versions of the array and every other variable, but you don’t need two different classes for each. To make an immutable array in Swift, you just assign it to a constant with let. If you want to make a mutable array in Swift, you just assign it to a variable using the keyword var:

var mutableArray = [1,2,3,4,5]
let immutableArray = [1,2,3,4,5]

The following is a comparison of Swift arrays and Objective-C NSArray and NSMutableArray:

Image

Modifying Arrays

Creating arrays is easy, but what can you do with them? You can append, insert, remove, and iterate over them. You can change them as long as they are mutable.

Accessing Array Elements

You can access elements from an array by using what Swift calls subscripts. You will learn much more about subscripting later, but for now, you just need to know that you can use the square brackets notation that you see in many other languages to access elements of an array. Here’s an example:

var myArray = [1,2,3,4]
myArray[0] // 1

Arrays start from an index of 0. Grabbing the 0th element will give you back the first element. You can also use the startIndex property of the array to find this out. You can also grab the total number of items in an array by using the count method:

myArray.count // 4

Adding Elements to an Array

If you have an array of prime numbers and want to add a new prime number to the list, you can use Swift’s append method, like this:

var primes = [2,3,5,7,11,13,17,19,23,29]
primes.append(31) // [2,3,5,7,11,13,17,19,23,29,31]


Note

If you have appended to an array in Python before, you know that Python also uses append to add to arrays. You will see some things in Swift from other languages from time to time.


You can also use += to easily concatenate two arrays:

raining += ["dogs","pigs","wolves"] // ["cats","dogs","pigs","wolves"]

When you append to an array, you are adding an element to the end of an array. The element you append will always become the last element.

If you want to add an element at the beginning of the array, you can use insert. Maybe it’s raining dogs and cats instead of cats and dogs:

var raining = ["cats"]
raining.insert("dogs",atIndex: 0)
raining // ["dogs","cats"]

Removing Elements from Arrays

If you want to remove items from an array, you can use a number of methods in Swift. For example, you can remove the last item with removeLast():

raining.removeLast()

And you can remove an element at a specific index by using removeAtIndex(:atIndex):

var raining = ["cats","octopuses"] // cats, octopuses
raining.insert("dogs", atIndex: 1)
raining // cats, dogs, octopuses
raining.removeAtIndex(1) // returns the element it removed "dogs"
raining // cats, octopuses

Here’s what’s going on here:

1. You start with an array of two elements.

2. You insert an element at index 1.

3. Then you just remove the same item you added at index 1.

When removing elements from an array it is important to remember that it works like a deck of cards. If you remove the third card from a deck, the fourth card becomes the third card and so on with every card below the fourth card. The second card does stay in the second position.

You can also do the following:

■ Create arrays with any type

■ Create arrays of mixed types

■ Add and remove items at the end of the array

Iterating Over Arrays

When you iterate over an array, you start at the beginning of the array and access each element of the array until you get to the end. Oftentimes you are looking for an element that meets a certain condition. Sometimes you will successfully find that element, and you will not need to iterate any further, so you will break the loop. The for-in loop is well suited for interating over arrays. Here’s what it looks like:

for animal in raining {
println(animal)
}
// cats
// octopuses

Sometimes you need to access the current index for tracking purposes. For this purpose, Swift provides a global enumerate function, which gives you access to the current index and current element. Here’s how it works:

for (i,animal) in enumerate(raining) {
println("Animal number \(i) is the \(animal)")
}
// Animal number 0 is the cats
// Animal number 1 is the octopuses

Extra Bits of Arrays

You can create arrays that are empty or with prepopulated contents. You can create and prepopulate an array by using the extra parameters extra: and repeatedValue:. Here’s an example:

var mapRow1 = [Int](count:10,repeatedValue:0) // [0,0,0,0,0,0,0,0,0,0,0]

Here we created a map for a game. If you were using this map for a game, you could place arrays within arrays to create a multidimensional array. You can use one array for each row and place that in one big array, like this:

var mapRow1 = [Int](count:10,repeatedValue:0)
var cols = 10
var rows = 10
var map = [[Int]]()
for row in 0..<rows {
var newRow = [Int](count:cols, repeatedValue:0)
map.append(newRow)
}
map

In this example, you create an array within an array. So the type of map is [[Int]], which means an array of arrays of Ints. Because [Int] is an array of Ints, wrapping that in square brackets will give you an array of Ints. This type of multidimensional array can be used in games to create a sort of tile map. Maybe for this map 0 is ground, 1 is road, and 2 is tree. The preceding example makes a whole map of ground. Notice that it does so without making a nested for loop.

Emptying an Array

You can completely empty an array by setting it equal to []: This technique is used in other languages.

map = []
map // 0 Elements

Using Dictionaries

Dictionaries are similar to arrays in that they are both containers that store multiple values of the same type. Dictionaries are different from arrays in that each value is stored with a key. You use that key to access the value. In arrays, you access elements by index. Arrays are stored in a specific order, and dictionaries are not. Just like arrays, though, dictionaries want to know what type you will be storing for their values. They also want to know what type you will use for its keys. You can write a dictionary in verbose form or shorthand. First the verbose:

var people:Dictionary<Int,String> = [186574663:"John Smith",
198364775:"Francis Green",
176354888:"Trevor Kalan"]
people[176354888] // {Some "Trevor Kalan"}

This dictionary is of type [Int : String]. Again, you can three-finger-click or tap the variable name to find the type of the dictionary. The keys are of type Int, and the values are of type String. There are a couple things to note here. For one thing, you can access the dictionary by using the same syntax that you use to access arrays. Note that by accessing the dictionary, Swift returns an optional (see Chapter 1). Why does Swift return an optional? It is possible that you are trying to access something that is not there. If you were sure that there is a value at the key you were accessing, you could force the value out with an exclamation point, like this:

people[176354888]! // "Trevor Kaleface"

Of course, there is a shorthand way to write dictionaries since Swift can automatically infer the types of dictionaries. You can rewrite a dictionary without an explicit type, like this:

var people = [186574663:"John Smith",
198364775:"Francis Green",
176354888:"Trevor Kaleface"]

It’s still the same dictionary as before.

Adding, Removing, and Inserting with Dictionaries

Previously you used the subscript syntax (the square brackets syntax) to access elements of a dictionary. You can use that syntax to also set values by key. In the previous example, you can set a person with a Social Security number of 384958338:

people[384958338] = "Skip Wilson"

If the key exists, then you will replace that Social Security value with Skip Wilson. If not, you will have a new key/value pair. Try to assign a key using a string, and you get an error.

You can also use the method updateValue(forKey:) to update your dictionary. This method also updates a value for a key if it exists, or it creates a new key value if it does not exist. updateValue returns an optional, which is the old value it replaced, or nil if it did not replace anything.

To remove items from a dictionary, you can just assign it to nil:

people[384958338] = nil

Now the person with Social Security number 384958338 is removed from the dictionary. You can also use removeValueForKey to do the same thing. It returns the old value it removed if the key exists. If not, it returns nil. It is otherwise known as an optional, and it looks like this:

people.removeValueForKey(176354888) // {Some "3343"}
people.removeValueForKey(24601) // nil

Iterating Over Dictionaries

You can iterate over a dictionary much the same way that you iterate over an array—by using a for-in loop. The only difference between an array for-in loop and a dictionary for-in loop is that with the dictionary loop, you are able to get both keys and values while looping, like this:

for (ssn,name) in people {
println("SSN: \(ssn) Name: \(name)")
}
// SSN: 198364775 Name: Francis Green
// SSN: 176354888 Name: Trevor Kalan
// SSN: 186574663 Name: John Smith

You can also loop through just the keys of a dictionary, with .keys. In addition, you can loop though just the values with .values:

for ssn in people.keys {
println("SSN: \(ssn)")
}
for name in people.values {
println("Name: \(name)")
}

Extra Bits of Dictionaries

You can create a new dictionary like this:

var vehicles = Dictionary<String,String>()

It is worth noting that once again, if you Command+click on the dictionary, you will see that it is made up of a Struct and subscripts. The <> characters tell you that the dictionary is made with generics, and it will accept any typeat all for its keys and values. (Generics are a powerful feature of Swift. and you will learn much more about them in Chapter 9, “Becoming Flexible with Generics.”)

If you want to count the number of key/value pairs in a dictionary, you can use .count:

people.count // 3

Emptying a Dictionary

You can empty a dictionary just by calling [:], like this:

people = [:] // 0 key/value pairs

Testing Dictionaries for the Presence of Values

Dictionaries return optionals when you try to access items. Therefore, you might want to place them into value bindings. If you don’t need the value of the key you are testing, you can instead use a regular if statement, like this:

if people[198364775] {
println("Got him")
} else {
println("No one with that Social Security number")
}

If you do in fact want the unwrapped value from the optional (if it does succeed), you can use full value binding, like this:

if let person = people[198364775] {
println("Got \(person)")
} else {
println("No one with that social security number")
}

Now you will have the unwrapped value of the optional available to you if there is a value to be had.

Putting It All Together

Next, you will create a little program from all the massive amounts of knowledge you have acquired thus far. Enter the code in Listing 2.1.

Listing 2.1 A Complete Example


import Foundation

let city = "Boston"
let trainName = "the Red Line"

var subwayStops = [
// Stop name and busyness on a scale of 1-10
("Harvard Square", 6),
("Kendall / MIT", 5),
("Central Square", 7),
("Charles MGH", 4),
("Park Street", 10)
]

var passengers = 0

for i in 0..<subwayStops.count {
var (stopName, busyness) = subwayStops[i]
// New passengers boarding the train
var board:Int

switch (busyness) {
case 1...4: board = 15
case 5...7: board = 30
case 7..<9: board = 45
case 10: board = 50
default: board = 0
}

// Some passengers may leave the train at each stop
let randomNumber = Int(arc4random_uniform(UInt32(passengers)))

//Ensure that passengers never becomes negative
if randomNumber < passengers {
passengers -= randomNumber
println("\(randomNumber) leave the train")
}

passengers += board
println("\(board) new passengers board at \(stopName)")
println("\(passengers) current on board")
}
println("A total of \(passengers) passengers were left on \(trainName) in \(city)")


You can paste this code directly into the playground so you can step through it:

■ Line 3: You create a city constant (Boston, in this case).

■ Line 4: You create a train name constant.

■ Lines 6–13: You create the subway stops array. This is of type [(String, Int)], which means an array of tuples in which the tuples are of types String and Int.


Note

For bonus points, make the array into an array of named tuples or reimplement it as a dictionary.


■ Line 15: You specify the current number of passengers.

■ Line 17: This is the main game loop, which loops from 0 to the number of subway stops.

■ Line 18: You grab values out of the tuples smoothly and simultaneously.

■ Line 22: You specify a switch with ranges, based on the busyness of the current stop.

■ Lines 23–26: If the busyness level is between x to y, you board z number of people.

■ Line 27: You need a default because the switch is not exhaustive. Your passengers might be exhausted, though.

■ Line 31: You choose a random number of passengers between 0 and the number of passengers current on the train to leave the train at each stop.

■ Line 34: You have to make sure randomNumber is less than the current number of passengers. You make those people leave the train.

■ Line 39: You choose the number of passengers to board from the switch statement.

■ Line 40: You print the number of new passengers.

■ Line 41: You print the number on board.

■ Line 44: You finish the game with the total number of passengers left on board.

Summary

With arrays and dictionaries you are able to store data in many different ways. They are like the tools in a carpenter’s kit. Arrays and dictionaries are an essential asset to successful Swift programming. Swift has given us multiple ways of accessing, adding, and removing items from these collections. The ways that you can use collections is up to you, and you will find there are many different uses for them.