Mastering Swift
上QQ阅读APP看书,第一时间看更新

Arrays

Arrays are a very common component of modern programming languages and can be found virtually in all modern programming languages. In Swift, arrays are an ordered list of objects of the same type. This is different from the NSArray class in Objective-C, which can contain objects of different types.

When an array is created, we must declare the type of data to be stored in it by explicit type declaration or through type inference. Typically, we only explicitly declare the data type of an array when we are creating an empty array. If we initialize an array with data, we should let the compiler use type inference to infer the most appropriate data type for the array.

Each object in an array is called an element. Each of these elements is stored in a set order and can be accessed by its location (index) in the array.

Creating and initializing arrays

We can initialize an array with an array literal. An array literal is a set of values that we prepopulate the array with. The following example shows how to define a nonmutable array of Integers, using the let keyword:

let arrayOne = [1,2,3]

As we mentioned, if we need to create a mutable array, we will use the var keyword to define the array. The following example shows how to define a mutable array:

var arrayTwo = [4,5,6]

In the preceding two examples, the compiler inferred the type of values stored in the array by looking at the type of values stored in the array literal. If we needed to create an empty array, we will need to explicitly declare the type of values to store in the array. The following example shows how to declare an empty array that can be used to store integers:

var arrayThree = [Int]()

In the preceding examples, we created arrays with integer values, and the majority of the array examples in this chapter will also use integer values; however ,we can create arrays in Swift with any type. The only rule is that once an array is defined as containing a particular type, all elements in the array must be of that type. The following example shows how we would create arrays of various data types:

var arrayOne = [String]()
var arrayTwo = [Double]()
var arrayThree = [MyObject]()

Swift does provide special type aliases for working with nonspecific types. These aliases are AnyObject and Any. We can use these aliases to define arrays whose elements are of different types like this:

var myArray: [AnyObject] = [1,"Two"]

We should use the Any and AnyObject aliases only when there is an explicit need for this behavior. It is always better to be specific about the types of data our collections contain.

We can also initialize an array to a certain size with all elements of the array set to a predefined value. This can be very useful if we want to create an array and prepopulate it with default values. The following example defines an array with seven elements, and each element contains the number three:

var arrayFour = [Int](count: 7, repeatedValue: 3)

While the most common array is a one-dimensional array, we can also create multidimensional arrays. A multidimensional array is really nothing more than an array of arrays. For example, a two-dimensional array is an array of arrays, while a three-dimensional array is an array of arrays of arrays. The following examples show two ways to create a two-dimensional array in Swift:

var multiArrayOne = [[1,2],[3,4],[5,6]]
var multiArrayTwo = [[Int]]()

Accessing the array elements

We use the subscript syntax to retrieve values from an array. The subscript syntax is where a number appears between two square brackets and that number specifies the location (index), within the array, of the element we wish to retrieve. The following example shows how to retrieve elements from an array using the subscript syntax:

let arrayOne = [1,2,3,4,5,6]
println(arrayOne[0])  //Displays '1'
println(arrayOne[3])  //Displays '4'

In the preceding code, we begin by creating an array of integers that contains six numbers. We then print out the value at index 0 and 3.

If we want to retrieve an inpidual value within a multidimensional array, we will need to provide a subscript for each dimension. If we did not provide a subscript for each dimension, then we will return an array rather than an inpidual value within the array. The following example shows how we can define a two-dimensional array and retrieve an inpidual value within the two dimensions:

var multiArray = [[1,2],[3,4],[5,6]]
var arr = multiArray[0] //arr containcontaincontains the array [1,2]
var value = multiArray[0][1] //value containcontaincontains 2

In the preceding code, we begin by defining a two-dimensional array. When we retrieve the value at index 0 of the first dimension (multiArray[0]), we retrieve the array [1,2]. When we retrieve the value at index 0 of the first dimension and index 1 of the second dimension (multiArray[0][1]), we retrieve the integer 2.

We can retrieve the first and last elements of an array using the first and last properties. The first and last properties return an optional value since the values may be nil if the array is empty. The following example shows how to use the first and last properties to retrieve the first and last elements of both single-dimensional and multi-dimensional arrays:

let arrayOne = [1,2,3,4,5,6]
var first = arrayOne.first  //first contains 1
var last = arrayOne.last  //last contains 6

let multiArray = [[1,2],[3,4],[5,6]]
var arrFirst1 = multiArray[0].first //arrFirst1 contains 1
var arrFirst2 = multiArray.first //arrFirst2 contains[1,2]
var arrLast1 = multiArray[0].last //arrLast1 contains 2
var arrLast2 = multiArray.last  //arrLast2 contains [5,6]

Counting the elements of an array

At times, it is essential to know the number of elements in an array. To retrieve the number of elements, we would use the read-only count property. The following example shows how to use this property to retrieve the number of elements in both single-dimensional and multi-dimensional arrays.

let arrayOne = [1,2,3]
let multiArrayOne = [[3,4],[5,6],[7,8]]
println(arrayOne.count)  //Displays 3
println(multiArrayOne.count)  //Displays 3 for the three arrays
println(multiArrayOne[0].count)  //Displays 2 for the two elements

The value that is returned by the count property is the number of elements in the array and not the largest valid index of the array, except when the number of elements in the array is 0 (zero). For nonempty arrays, the largest valid index is the number of elements in the array minus one. This is because the first element of the array has an index number of zero. As an example, if an array has two elements, the valid indexes are 0 and 1, while the count property would return 2. The following code illustrates this:

let arrayOne = [0,1]
println(arrayOne[0])  //Displays 0
println(arrayOne[1])  //Displays 1
println(arrayOne.count) //Displays 2

If we attempt to retrieve an element from an array, using the subscript syntax, where the index is outside the range of the array, the application will throw an Array index out of range error. Therefore, if we are unsure of the size of an array, it is good practice to verify that the index is not outside the range of the array. The following examples illustrate this concept:

//This example will throw an array index out of range error
var arrayTwo = [1,2,3,4]
println(arrayTwo[6])

//This example will not throw an array index out of range error
var arrayOne = [1,2,3,4]
if (arrayOne.count > 6) {
    println(arrayOne[6])
}

In the preceding code, the first block of code would throw an array index out of range error exception because we are attempting to access the value from the array arrayTwo at index 6; however, there are only four elements in the array. The second example would not throw the array index out of range error exception because we are checking whether the arrayOne array contains more than six elements, and if it does not, we do not attempt to access the value at index 6.

Is the array empty?

To check whether an array is empty (does not contain any elements), we use the isEmpty property. This property will return true if the array is empty, or false if the array has elements. The following example shows how to check whether an array is empty or not:

var arrayOne = [1,2]
var arrayTwo = [Int]()
arrayOne.isEmpty  //Returns false because the array is not empty
arrayTwo.isEmpty  //Returns true because the array is empty

Appending to an array

A static array is somewhat useful but having the ability to add elements dynamically is what makes arrays really useful. To add an item to the end of an array, we can use the append method. The following example shows how to append an item to the end of an array:

var arrayOne = [1,2]
arrayOne.append(3)  //arrayOne will now contain 1, 2 and 3

Swift also allows us to use the addition assignment operator (+=) to append an array to another array. The following example shows how to use the addition assignment operator to append an array to the end of another array:

var arrayOne = [1,2]
arrayOne += [3,4]  //arrayOne will now contain 1, 2, 3 and 4

Which way you append an element to the end of an array is really up to you. Personally, I prefer the assignment operator because, to me, it is a bit easier to read, but we will be using both in this book.

Inserting a value into an array

We can insert a value into an array by using the insert method. The insert method will move all items, starting at the specified index up one spot, to make room for the new element, and then inserts the value into the specified index. The following example shows how to use the insert method to insert a new value into an array:

var arrayOne = [1,2,3,4,5]
arrayOne.insert(10, atIndex: 3) //arrayOne now contains 1, 2, 3, 10, 4 and 5
Note

You cannot insert a value that is outside the current range of the array. Attempting to do so will throw an Index out of range exception. For example, in the preceding code, if we attempt to insert a new integer at index 10, we will receive an Index out of range exception because arrayOne only contains five elements. The exception to this is that we are able to insert an item directly after the last element; therefore, we can insert an item at index six. However, it is recommended that we use the append function to append an item to avoid errors.

Replacing elements in an array

We use the subscript syntax to update elements in an array. Using the subscript, we pick the element of the array we wish to update and then use the assignment operator to assign a new value. The following example shows how we will replace a value in an array:

var arrayOne = [1,2,3]
arrayOne[1] = 10  //arrayOne now contains 1,10,3
Note

You cannot update a value that is outside the current range of the array. Attempting to do so will throw the same Index out of range exception that was thrown when we tried to insert a value outside the range of the array.

Removing elements from an array

There are three methods that we can use to remove one or all elements in an array. These methods are removeLast(), removeAtIndex(), and removeAll(). The following example shows how to use the three methods to remove elements from the array:

var arrayOne = [1,2,3,4,5]
arrayOne.removeLast()  //arrayOne now contains 1, 2, 3 and 4
arrayOne.removeAtIndex(2)  //arrayOne now contains 1, 2 and 4
arrayOne.removeAll()  //arrayOne is now empty

The removeLast() and removeAtIndex() methods will also return the value of the element that it is removing. Therefore, if we want to know the value of the item that was removed, we can rewrite the removeAtIndex and removedLast lines to capture the value, as shown in the following example:

var arrayOne = [1,2,3,4,5]
var removed1 = arrayOne.removeLast()  //removed1 contains 5
var removed = arrayOne.removeAtIndex(2)  //removed contains 3

Adding two arrays

To create a new array by adding two arrays together, we use the addition (+) operator. The following example shows how to use the addition (+) operator to create a new array that contains all the elements of two other arrays:

let arrayOne = [1,2]
let arrayTwo = [3,4]
var combine = arrayOne + arrayTwo //combine contains 1, 2, 3 and 4

In the preceding code, arrayOne and arrayTwo are left unchanged, while the combine array contains the elements from arrayOne, followed by the elements from arrayTwo.

Reversing an array

We can create a new array from the original array with the elements in reverse order using the reverse() method. The original array will remain unchanged by the reverse method. The following example shows how to use the reverse() method:

var arrayOne = [1,2,3]
var reverse = arrayOne.reverse() //reverse contains 3,2 and 1

In the preceding code, the elements of arrayOne are left unchanged, while the reverse array will contain all the elements from arrayOne but in the reverse order.

Retrieving a subarray from an array

We can retrieve a subarray from an existing array by using the subscript syntax with a range. The following example shows how to retrieve a range of elements from an existing array:

let arrayOne = [1,2,3,4,5]
var subArray = arrayOne[2…4] //subArray contains 3, 4 and 5

The operator (three periods) is known as a range operator. The range operator, in the preceding code, says I want all elements, 2 through 4, inclusively (included elements 2 and 4 as well as what is between them). There is another range operator, which is .< the same as the range operator but it excludes the last element. The following example shows how to use the .< operator.

let arrayOne = [1,2,3,4,5]
var subArray = arrayOne[2.<4] //subArray contains 3 and 4

In the preceding example, the subArray will contain two elements 3 and 4.

Making bulk changes to an array

We can use the subscript syntax with a range operator to change the values of multiple elements. The following example shows how to use the subscript syntax to change a range of elements.

var arrayOne = [1,2,3,4,5]
arrayOne[1…2] = [12,13]//arrayOne contains 1,12,13,4 and 5

In the preceding code, the elements at index 1 and 2 will be changed to numbers 12 and 13. After this, when the code runs, arrayOne will contain 1, 12, 13, 4, and 5.

The number of elements that you are changing in the range operator does not need to match the number of values that you are passing in. Swift makes the bulk changes as follows—it first removes the elements defined by the range operator and then inserts the new values. The following example demonstrates this concept:

var arrayOne = [1,2,3,4,5]
arrayOne[1.3] = [12,13]
//arrayOne now contains 1, 12, 13 and 5 (four elements)

In the preceding code, arrayOne starts with five elements. We then say that we want to replace the range of elements 1 through 3 inclusively. This causes elements 1 through 3 (three elements) to be removed from the array. We then add two elements (12 and 13) to the array, starting at index 1. After this is complete, arrayOne will contain the following four elements: 1, 12, 13, and 5. Let's see what happens if we try to add more elements than we are removing:

var arrayOne = [1,2,3,4,5]
arrayOne[1.3] = [12,13,14,15]
//arrayOne now contains 1, 12, 13, 14, 15 and 5 (six elements)

In the preceding code, arrayOne starts with five elements. We then say that we want to replace the range of elements 1 through 3 inclusively. This causes elements 1 through 3 (three elements) to be removed from the array. We then add four elements (12, 13, 14, and 15) to the array, starting at index 1. After this is complete, arrayOne will contain the following six elements: 1, 12, 13, 14, 15, and 5.

Algorithms for arrays

Swift arrays have several methods that take a closure as the argument. These methods transform the array and the closures affect how the array is transformed. Closures are self-contained blocks of code that can be passed around, and are similar to blocks in C and Objective-C and lambdas in other languages. We will discuss closures in depth in Chapter 10, Working with Closures. For now, we just want to get familiar with how the algorithms work in Swift.

sort

The sort algorithm sorts the array in place. This means when the sort method is used, the original array is replaced by the sorted array. The closure takes two arguments (represented by $0 and $1), and it should return a Boolean value that indicates whether the first element should be placed before the second element. The following code shows how to use the sort algorithm:

var arrayOne = [9,3,6,2,8,5]
arrayOne.sort{ $0 < $1 }
//arrayOne contains 2,3,5,6,8 and 9

The preceding code will sort the array in increasing order. We can tell this because our rule will return true if the first number ($0) is less than the second number ($1). Therefore, when the sort algorithm begins, it compares the first two numbers (9 and 3) and returns true if the first number (9) is less than the second number (3). In our case, the rule returns false, so the numbers are reversed. The algorithm continues sorting, in this manner, until all of the numbers are sorted.

The preceding example sorted the array in numerically increasing order, if we wanted to reverse the sort order, we will reverse the arguments in the closure. The following code shows how to reverse the sort order:

var arrayOne = [9,3,6,2,8,5]
arrayOne.sort{ $1 < $0 }
//arrayOne contains 9,8,6,5,3 and 2

When we run this code, arrayOne will contain the elements 9, 8, 6, 5, 3, and 2.

sorted

While the sort algorithm sorts the array in place (replaces the original array), the sorted algorithm does not change the original array, but instead creates a new array with the sorted elements from the original array. The following example shows how to use the sorted algorithm:

var arrayOne = [9,3,6,2,8,5]
let sorted = arrayOne.sorted{ $0 < $1 }
//sorted contains 2,3,5,6,8 and 9
//arrayOne contains 9,3,6,2,8 and 5

When we run this code, arrayOne will contain the original unsorted array (9, 3, 6, 2, 8, and 5) and the sorted array will contain the new sorted array (2, 3, 5, 6, 8, and 9).

filter

The filter algorithm will return a new array by filtering the original array. This is one of the most powerful array algorithms and may end up to be the one we use the most. If we need to retrieve a subset of an array, based on set up rules, I recommend using this algorithm, rather than trying to write your own method to filter the array. The closure takes one argument and it should return a Boolean true, if the element should be included in the new array, as shown in the following code;

var arrayOne = [1,2,3,4,5,6,7,8,9]
let filtered = arrayOne.filter{$0 > 3 && 0 < 7}
//filtered contains 4,5 and 6

In the preceding code, the rule that we are passing to the algorithm returns true, if the number is greater than 3 or less than 7; therefore, any number that is greater than 3 or less than 7 is included in the new filtered array.

Let's look at another example; this one shows how we can retrieve a subset of cities that contain the letter 'o' in their name from an array of cities:

var city = ["Boston", "London", "Chicago", "Atlanta"]
let filtered = city.filter{$0.rangeOfString("o") != nil}
//filtered contains "Boston", "London" and "Chicago"

In the preceding code, we use the rangeOfString() method to return true, if the string contains the letter 'o'. If the method returns true, then the string is included in the filtered array.

map

The map algorithm returns a new array that contains the results of applying the rules in the closure to each element of the array. The following example shows how to use the map algorithm to pide each number by 10.

var arrayOne = [10, 20, 30, 40]
let applied = arrayOne.map{ $0 / 10}
//applied contains 1,2,3 and 4

In the preceding code, the new array contains the numbers 1, 2, 3, and 4, which is the result of piding each element of the original array by 10.

The new array created by the map algorithm is not required to contain the same element types as the original array; however, all the elements in the new array must be of the same type. In the following example, the original array contains integer values, but the new array created by the map algorithm contains string elements.

var arrayOne = [1, 2, 3, 4]
let applied = arrayOne.map{ "num:\($0)"}
//applied contains "num:1", "num:2", "num:3" and "num:4"

In the preceding code, we created an array of strings that appends the numbers from the original array to the string num:.

Iterating over an array

We can iterate over all elements of an array, in order, with a for-in loop. The for-in loop will execute one or more statements for each element of the array. The following example shows how we would iterate over the elements of an array:

var arr = ["one", "two", "three"]
for item in arr {
    println(item)
}

In the preceding example, the for-in loop iterates over the arr array and executes the println(item) line for each element in the array. If we run this code, it will display the following results in the console:

one
two
three

There are times when we would like to iterate over an array, as we did in the preceding example, but we would also like to know the index of the element as well as the value of the element. To do this, we can use the enumerate function, which returns a tuple (see Tuples later in this chapter) for each item in the array that contains both the index and value of the element. The following example shows how to use the enumerate function.

var arr = ["one", "two", "three"]
for (index,value) in enumerate(arr) {
    println("\(index) \(value)")
}

The previous code will display the following results in the console:

0) one
1) two
2) three

Now that we have introduced arrays in Swift, let's look at what a dictionary is.