Difference between revisions of "W2021 Two-Dimensional Arrays"

From Coder Merlin
(Created page with "Two-dimensional arrays are Swift arrays whose elements are other arrays. However, this is not the only interesting property of two-dimensional arrays. Two-dimensional arrays i...")
 
Line 1: Line 1:
Two-dimensional arrays are Swift arrays whose elements are other arrays. However, this is not the only interesting property of two-dimensional arrays. Two-dimensional arrays in Swift are very similar to matrices in math.
As previously mentioned in [[W1301_Arrays]], an array is a variable that holds many elements of the same/homogeneous type. A two-dimensional array follows the same principles, with the elements of the array being more arrays. Each of those arrays in turn have elements of the same type.


The benefit of using two-dimensional arrays is that they are very efficient when working with data organized in rows and columns. The problem with using one-dimensional arrays is that they are not efficient when working with data organized this way.
Two-dimensional arrays are especially useful when representing data that is in rows and columns, or a conceptually similar format such as matrices. For example, here is a two-dimensional array with 9 rows and 10 columns:


As an example, consider the following one-dimensional array representing a deck of cards where each index represents a card:
<syntaxhighlight>
[["R1C1", "R1C2", "R1C3", "R1C4", "R1C5", "R1C6", "R1C7", "R1C8", "R1C9", "R1C10"],
["R2C1", "R2C2", "R2C3", "R2C4", "R2C5", "R2C6", "R2C7", "R2C8", "R2C9", "R2C10"],
["R3C1", "R3C2", "R3C3", "R3C4", "R3C5", "R3C6", "R3C7", "R3C8", "R3C9", "R3C10"],
["R4C1", "R4C2", "R4C3", "R4C4", "R4C5", "R4C6", "R4C7", "R4C8", "R4C9", "R4C10"],
["R5C1", "R5C2", "R5C3", "R5C4", "R5C5", "R5C6", "R5C7", "R5C8", "R5C9", "R5C10"],
["R6C1", "R6C2", "R6C3", "R6C4", "R6C5", "R6C6", "R6C7", "R6C8", "R6C9", "R6C10"],
["R7C1", "R7C2", "R7C3", "R7C4", "R7C5", "R7C6", "R7C7", "R7C8", "R7C9", "R7C10"],
["R8C1", "R8C2", "R8C3", "R8C4", "R8C5", "R8C6", "R8C7", "R8C8", "R8C9", "R8C10"],
["R9C1", "R9C2", "R9C3", "R9C4", "R9C5", "R9C6", "R9C7", "R9C8", "R9C9", "R9C10"]]
</syntaxhighlight>


<syntaxhighlight lang="swift">
It is worth noting that the indexes for the array still start at zero; however, there is generally no row 0 or column 0, so the elements have been incremented to represent this.
var deckOfCards = [Card]()
</syntaxhighlight>


The deck of cards is not useful yet because it is empty. Now suppose you need to fill the first four cards with data. This could be done as follows:
== Defining A Two-Dimensional Array In Swift ==
=== Without A Predefined Size ===
To create a two-dimensional array without a predefined size, a similar construct to a single-dimensional array may be used:


<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
 
var twoDim = [[String]]()
deckOfCards.append(Card(rank: .Ace, suit: .Spades)) // 1st card
 
deckOfCards.append(Card(rank: .Two, suit: .Clubs)) // 2nd card
 
deckOfCards.append(Card(rank: .Three, suit: .Diamonds)) // 3rd card
 
deckOfCards.append(Card(rank: .Four, suit: .Hearts)) // 4th card
 
To access the data in any of these cards you would have to write code similar to this:
 
var firstCard = deckOfCards.first
 
var secondCard = deckOfCards.dropFirst().first
 
</syntaxhighlight>
</syntaxhighlight>


The <syntaxhighlight lang="swift" inline> .first </syntaxhighlight>
Note the double brackets '''[ [''' and '''] ]''' which define this as a two-dimensional array. The first level of the array is an array that can hold an array that holds strings. Data can be written to such an array using '''append''' as usual:
property returns the first element of a Swift array, which is not very intuitive if you are not familiar with arrays. Similarly, the
<syntaxhighlight lang="swift" inline>.dropFirst()</syntaxhighlight> method returns a new array without the first element. This means that if you want to get any of the data stored in these 4 cards, you need to know both the indices of this data and which method was used to drop the first element of an array (in this case .first).
 
== Curriculum ==
{{MerlinCurriculumData|{{ROOTPAGENAME}}}}
 
== Motivation for Using Two-Dimensional Arrays in Swift==
 
The newer versions of Swift bring many improvements and enhancements to the language, which help beginners and professionals alike write better code with less effort, leading to a more pleasant user experience. One of these new features is the possibility of declaring arrays of an arbitrary type, rather than only of the types that have been available since Swift was first introduced. However, this new feature doesn't provide any new functionality, but rather it makes existing code easier to write and maintain.
 
Most programming languages that support arrays of arbitrary type use some form of the generic array where each element must be of the same type. For example, when using Swift to create an array with three i32 elements, one would write:  


<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
let intArray: [Int] = [1, 2, 3]
twoDim.append(["This", "is", "an", "array"])
twoDim.append(["This", "is", "another", "array"])
</syntaxhighlight>
</syntaxhighlight>


This array is polymorphic in the sense that it can hold any integer value. However, when dealing with arrays containing objects of different types, this type-safety must be relaxed in order to avoid a situation where the compiler forces us to add a cast for each object contained inside the array.
=== With A Predefined Size ===
 
In order to create an array with a predefined size, you can use the '''Array''' constructor:
Even though the compiler is aware of each object's type being inserted into the array, it does not flag any errors when these lines are compiled. Two-dimensional arrays allow us to work around this problem by declaring a type for each of its dimensions, i.e., each row and column.
 
By doing so, the compiler will raise an error if the types contained in a given row or column do not match those declared for that dimension. This allows us to have greater control over the creation of arrays whose elements consist of an arbitrary number of different types.
 
==Declaring and Initializing Multi-Dimensional Arrays in Swift==
 
As with single-dimensional arrays, multi-dimensional arrays can be initialized either at the declaration or upon initialization.
 
In this case, the compiler will raise an error if either of these dimensions is not a positive integer.
 
However, as with any value type of Array type, it is possible to initialize the array with any given set of values enclosed in an array literal or dictionary literal.
 
{{CodeExplorer
|exerciseID=1
|mode=swift
|height=300
|initialCode=import Foundation
 
var multiDimensionalArray = [[1, 2], [3, 4]]
 
multiDimensionalArray = [["Hello", "World"], ["Swift", "is fun"]]
 
}}
 
One thing to keep in mind when initializing an array of arrays (i.e., a two-dimensional array) is that the inner arrays must all have the same length. This is because multi-dimensional arrays are just a special case of Array where each element is another Array.
 
However, unlike other programming languages that support two-dimensional arrays, Swift doesn't allow us to increase the size of an array by adding new rows or columns after its creation. Rather, it forces us to either initialize the array with a predefined length or add elements one at a time.


<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
let intArray: [Int] = [1, 2, 3]
var twoDim = Array(repeating: Array(repeating: "", count: 10), count: 10)
</syntaxhighlight>
</syntaxhighlight>


This array is polymorphic in the sense that it can hold any integer value. However, when dealing with arrays containing objects of different types, this type-safety must be relaxed in order to avoid a situation where the compiler forces us to add a cast for each object contained inside the array.
This will create an array with 10 elements, each of which is an array of 10 strings.
 
Even though the compiler is aware of each object's type being inserted into the array, it does not flag any errors when these lines are compiled. Two-dimensional arrays allow us to work around this problem by declaring a type for each of its dimensions, i.e., each row and column.
 
By doing so, the compiler will raise an error if the types contained in a given row or column do not match those declared for that dimension. This allows us to have greater control over the creation of arrays whose elements consist of an arbitrary number of different types.


==Declaring and Initializing Multi-Dimensional Arrays in Swift==
=== With A Predefined Array ===
 
If the two-dimensional array is already known (or at least a default), then it can be assigned to a variable just like a one-dimensional array:
As with single-dimensional arrays, multi-dimensional arrays can be initialized either at the declaration or upon initialization.
In this case, the compiler will raise an error if either of these dimensions is not a positive integer.
 
However, as with any value type of Array type, it is possible to initialize the array with any given set of values enclosed in an array literal or dictionary literal.
 
{{CodeExplorer
|exerciseID=2
|mode=swift
|height=300
|initialCode=import Foundation
 
var multiDimensionalArray = [[1, 2], [3, 4]]
 
multiDimensionalArray = [["Hello", "World"], ["Swift", "is fun"]]
 
}}
 
One thing to keep in mind when initializing an array of arrays (i.e., a two-dimensional array) is that the inner arrays must all have the same length. This is because multi-dimensional arrays are just a special case of Array where each element is another Array.
 
However, unlike other programming languages that support two-dimensional arrays, Swift doesn't allow us to increase the size of an array by adding new rows or columns after its creation. Rather, it forces us to either initialize the array with a predefined length or add elements one at a time.
 
== Accessing Multi-Dimensional Arrays in Swift ==
 
One way of accessing an element from a two-dimensional array is by using subscript syntax, just as we would with any other type of array.
{{CodeExplorer
|exerciseID=3
|mode=swift
|height=300
|initialCode=import Foundation
 
var multiDimensionalArray = [[1, 2], [3, 4]]
 
multiDimensionalArray = [["Hello", "World"], ["Swift", "is fun"]]
 
print(multiDimensionalArray [2][1])
 
}}
 
However, subscripts are limited to one-based indexing. This means that the first row or column of a two-dimensional array starts at index 1 instead of 0.
 
The way around this problem is by using another form of subscript syntax with an optional base parameter (i.e., the number to start counting from) which defaults to zero.


<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
multiDimensionalArray [2][1] = 6 // this is equivalent to multiDimensionalArray [2].0 [1].0 = 6
var twoDim = [["R1C1", "R1C2", "R1C3", "R1C4", "R1C5", "R1C6", "R1C7", "R1C8", "R1C9", "R1C10"],
              ["R2C1", "R2C2", "R2C3", "R2C4", "R2C5", "R2C6", "R2C7", "R2C8", "R2C9", "R2C10"],
              ["R3C1", "R3C2", "R3C3", "R3C4", "R3C5", "R3C6", "R3C7", "R3C8", "R3C9", "R3C10"],
              ["R4C1", "R4C2", "R4C3", "R4C4", "R4C5", "R4C6", "R4C7", "R4C8", "R4C9", "R4C10"],
              ["R5C1", "R5C2", "R5C3", "R5C4", "R5C5", "R5C6", "R5C7", "R5C8", "R5C9", "R5C10"],
              ["R6C1", "R6C2", "R6C3", "R6C4", "R6C5", "R6C6", "R6C7", "R6C8", "R6C9", "R6C10"],
              ["R7C1", "R7C2", "R7C3", "R7C4", "R7C5", "R7C6", "R7C7", "R7C8", "R7C9", "R7C10"],
              ["R8C1", "R8C2", "R8C3", "R8C4", "R8C5", "R8C6", "R8C7", "R8C8", "R8C9", "R8C10"],
              ["R9C1", "R9C2", "R9C3", "R9C4", "R9C5", "R9C6", "R9C7", "R9C8", "R9C9", "R9C10"]]
</syntaxhighlight>
</syntaxhighlight>


The following code shows how we would access an item in a nested array of arrays, where some of the elements are initialized with values and others are simply placeholders.  
 
== Indexes ==
Similar to a one-dimensional array, every element in a two-dimensional array has a unique index. In addition, the syntax for retrieving and setting an element is the same. The only difference is that after the first index is passed, an array is returned. In order to access a particular element, two levels of indexes need to be used; one for the "outer" array and one for the "inner" array. For example, to retrieve the element at row 3 column 5, the indexes will be 2 and then 4 (because array indexing starts at zero):


<syntaxhighlight lang="swift">
<syntaxhighlight lang="swift">
multiDimensionalArray [1] = [[7, 8], 60] // this is equivalent to multiDimensionalArray .0 [1] .0 = [[7, 8], 60]
twoDim[2][4] // R3C5
</syntaxhighlight>
</syntaxhighlight>
The first multi-dimensional array has two dimensions ( i.e., each element is another array), and the first row has two elements. The first element of this first row is initialized by assigning it a value of 7 whereas the second element is initialized as a placeholder with the value 60.
==Traversal of Multi-Dimensional Arrays in Swift==
Swift has made multi-dimensional arrays much easier to manage. You can think of it as managing a grid where each row is an individual array, potentially with different lengths and the columns are indexes into the rows.
For example, you might have a 2D array where the first dimension is 3 elements long and the second dimension is 4 elements long. This would result in multiple arrays, one for each row in the grid.
Each of the inner arrays is a reference to an instance of that grid (i.e. an array). You can then adjust various elements in the multidimensional array like this:


{{CodeExplorer
{{CodeExplorer
|exerciseID=4
|exerciseID=1
|height=250
|mode=swift
|mode=swift
|height=300
|initialCode=
|initialCode=import Foundation
var twoDim = [["R1C1", "R1C2", "R1C3", "R1C4", "R1C5", "R1C6", "R1C7", "R1C8", "R1C9", "R1C10"],
 
              ["R2C1", "R2C2", "R2C3", "R2C4", "R2C5", "R2C6", "R2C7", "R2C8", "R2C9", "R2C10"],
var twoDArray = [1, 2, 3, 4]
              ["R3C1", "R3C2", "R3C3", "R3C4", "R3C5", "R3C6", "R3C7", "R3C8", "R3C9", "R3C10"],
 
              ["R4C1", "R4C2", "R4C3", "R4C4", "R4C5", "R4C6", "R4C7", "R4C8", "R4C9", "R4C10"],
twoDArray [1][3] = 5
              ["R5C1", "R5C2", "R5C3", "R5C4", "R5C5", "R5C6", "R5C7", "R5C8", "R5C9", "R5C10"],
 
              ["R6C1", "R6C2", "R6C3", "R6C4", "R6C5", "R6C6", "R6C7", "R6C8", "R6C9", "R6C10"],
// results in an updated array: twoDArray == [1, 2, 5, 4]
              ["R7C1", "R7C2", "R7C3", "R7C4", "R7C5", "R7C6", "R7C7", "R7C8", "R7C9", "R7C10"],
              ["R8C1", "R8C2", "R8C3", "R8C4", "R8C5", "R8C6", "R8C7", "R8C8", "R8C9", "R8C10"],
              ["R9C1", "R9C2", "R9C3", "R9C4", "R9C5", "R9C6", "R9C7", "R9C8", "R9C9", "R9C10"]]


print(twoDim[2][4])
}}
}}


This is a great way to work with multi-dimensional arrays.
== Loops ==
 
Similar to one-dimensional arrays, two-dimensional arrays are also commonly used within for loops. For example, to print each inner array on its own line, a single for loop can be used:
==Searching for an Element in a Multi-Dimensional Array in Swift==
 
You can then use this to search for an element in the multi-dimensional array. This will return a Subscript Index (an Element) which you can then either store and adjust:


{{CodeExplorer
{{CodeExplorer
|exerciseID=5
|exerciseID=2
|height=250
|mode=swift
|mode=swift
|height=300
|initialCode=
|initialCode=import Foundation
var twoDim = [["R1C1", "R1C2", "R1C3", "R1C4", "R1C5", "R1C6", "R1C7", "R1C8", "R1C9", "R1C10"],
 
              ["R2C1", "R2C2", "R2C3", "R2C4", "R2C5", "R2C6", "R2C7", "R2C8", "R2C9", "R2C10"],
var twoDArray = [1, 2, 3, 4]
              ["R3C1", "R3C2", "R3C3", "R3C4", "R3C5", "R3C6", "R3C7", "R3C8", "R3C9", "R3C10"],
 
              ["R4C1", "R4C2", "R4C3", "R4C4", "R4C5", "R4C6", "R4C7", "R4C8", "R4C9", "R4C10"],
let indexToFind = twoDArray.firstIndex(of: 5)!
              ["R5C1", "R5C2", "R5C3", "R5C4", "R5C5", "R5C6", "R5C7", "R5C8", "R5C9", "R5C10"],
 
              ["R6C1", "R6C2", "R6C3", "R6C4", "R6C5", "R6C6", "R6C7", "R6C8", "R6C9", "R6C10"],
// twoDArray is now [1, 2, 3, 4]  
              ["R7C1", "R7C2", "R7C3", "R7C4", "R7C5", "R7C6", "R7C7", "R7C8", "R7C9", "R7C10"],
              ["R8C1", "R8C2", "R8C3", "R8C4", "R8C5", "R8C6", "R8C7", "R8C8", "R8C9", "R8C10"],
              ["R9C1", "R9C2", "R9C3", "R9C4", "R9C5", "R9C6", "R9C7", "R9C8", "R9C9", "R9C10"]]


twoDArray [indexToFind] = 5
func prettyPrint(twoDim: [[String]]) {
 
    for row in 0..<twoDim.count {
// results in an updated array: twoDArray == [1, 2, 5, 4]
        print(twoDim[row])
    }
}


prettyPrint(twoDim: twoDim)
}}
}}


==Insertion of an Element in a Multi-Dimensional Array in Swift==
However, since each element of the outer array is itself an array, two levels of for loops are required to interact with each indivudal element. For example, in order to initialize '''twoDim''' with each row and column labelled, two for loops can be used to keep track of the row and then column:


You can insert an element into a multi-dimensional array at a specified index. This will then return the new index of that reference in the updated array:


{{CodeExplorer
{{CodeExplorer
|exerciseID=6
|exerciseID=3
|mode=swift
|height=250
|height=300
|initialCode=import Foundation
 
let twoDArray = [1, 2, 3]
 
var newIndex = twoDArray.insert("X", at: 1)
 
// newIndex is now 1
 
}}
 
==Deleting an Element in a Multi-Dimensional Array in Swift==
 
To delete an element from a multi-dimensional array, you need to know the index of the reference.
 
For example:
 
{{CodeExplorer
|exerciseID=7
|mode=swift
|mode=swift
|height=300
|initialCode=
|initialCode=import Foundation


let twoDArray = [1, 2, 3]
// Create a two-dimensional array with 9 rows and 10 columns
var twoDim = Array(repeating: Array(repeating: "", count: 10), count: 9)


twoDArray.removeAtIndex(2)
// Iterate through each row
for row in 0..<twoDim.count {
    // Iterate through each column
    for col in 0..<twoDim[row].count {
        twoDim[row][col] = "R\(row + 1)C\(row+1)"
    }
}


// results in an updated array: twoDArray == [1, 3]  
// Print each row on its own line
func prettyPrint(twoDim: [[String]]) {
    for row in 0..<twoDim.count {
        print(twoDim[row])
    }
}


prettyPrint(twoDim: twoDim)
}}
}}
==Conclusion==
To conclude,  multi-dimensional arrays in Swift are a lot easier to manage because of the support for Subscript Indices.
This allows you to work with data organized as a grid. You can search, update and delete elements from this grid very easily by knowing their indices.
In addition, having setter access to these multi-dimensional arrays is nice because you can adjust the size of the grid dynamically.
These are some points that I found really helpful when working with multi-dimensional arrays in Swift and working with these grids.

Revision as of 23:06, 31 October 2021

Within these castle walls be forged Mavens of Computer Science ...
— Merlin, The Coder

As previously mentioned in W1301_Arrays, an array is a variable that holds many elements of the same/homogeneous type. A two-dimensional array follows the same principles, with the elements of the array being more arrays. Each of those arrays in turn have elements of the same type.

Two-dimensional arrays are especially useful when representing data that is in rows and columns, or a conceptually similar format such as matrices. For example, here is a two-dimensional array with 9 rows and 10 columns:

[["R1C1", "R1C2", "R1C3", "R1C4", "R1C5", "R1C6", "R1C7", "R1C8", "R1C9", "R1C10"],
["R2C1", "R2C2", "R2C3", "R2C4", "R2C5", "R2C6", "R2C7", "R2C8", "R2C9", "R2C10"],
["R3C1", "R3C2", "R3C3", "R3C4", "R3C5", "R3C6", "R3C7", "R3C8", "R3C9", "R3C10"],
["R4C1", "R4C2", "R4C3", "R4C4", "R4C5", "R4C6", "R4C7", "R4C8", "R4C9", "R4C10"],
["R5C1", "R5C2", "R5C3", "R5C4", "R5C5", "R5C6", "R5C7", "R5C8", "R5C9", "R5C10"],
["R6C1", "R6C2", "R6C3", "R6C4", "R6C5", "R6C6", "R6C7", "R6C8", "R6C9", "R6C10"],
["R7C1", "R7C2", "R7C3", "R7C4", "R7C5", "R7C6", "R7C7", "R7C8", "R7C9", "R7C10"],
["R8C1", "R8C2", "R8C3", "R8C4", "R8C5", "R8C6", "R8C7", "R8C8", "R8C9", "R8C10"],
["R9C1", "R9C2", "R9C3", "R9C4", "R9C5", "R9C6", "R9C7", "R9C8", "R9C9", "R9C10"]]

It is worth noting that the indexes for the array still start at zero; however, there is generally no row 0 or column 0, so the elements have been incremented to represent this.

Defining A Two-Dimensional Array In Swift[edit]

Without A Predefined Size[edit]

To create a two-dimensional array without a predefined size, a similar construct to a single-dimensional array may be used:

var twoDim = [[String]]()

Note the double brackets [ [ and ] ] which define this as a two-dimensional array. The first level of the array is an array that can hold an array that holds strings. Data can be written to such an array using append as usual:

twoDim.append(["This", "is", "an", "array"])
twoDim.append(["This", "is", "another", "array"])

With A Predefined Size[edit]

In order to create an array with a predefined size, you can use the Array constructor:

var twoDim = Array(repeating: Array(repeating: "", count: 10), count: 10)

This will create an array with 10 elements, each of which is an array of 10 strings.

With A Predefined Array[edit]

If the two-dimensional array is already known (or at least a default), then it can be assigned to a variable just like a one-dimensional array:

var twoDim = [["R1C1", "R1C2", "R1C3", "R1C4", "R1C5", "R1C6", "R1C7", "R1C8", "R1C9", "R1C10"],
              ["R2C1", "R2C2", "R2C3", "R2C4", "R2C5", "R2C6", "R2C7", "R2C8", "R2C9", "R2C10"],
              ["R3C1", "R3C2", "R3C3", "R3C4", "R3C5", "R3C6", "R3C7", "R3C8", "R3C9", "R3C10"],
              ["R4C1", "R4C2", "R4C3", "R4C4", "R4C5", "R4C6", "R4C7", "R4C8", "R4C9", "R4C10"],
              ["R5C1", "R5C2", "R5C3", "R5C4", "R5C5", "R5C6", "R5C7", "R5C8", "R5C9", "R5C10"],
              ["R6C1", "R6C2", "R6C3", "R6C4", "R6C5", "R6C6", "R6C7", "R6C8", "R6C9", "R6C10"],
              ["R7C1", "R7C2", "R7C3", "R7C4", "R7C5", "R7C6", "R7C7", "R7C8", "R7C9", "R7C10"],
              ["R8C1", "R8C2", "R8C3", "R8C4", "R8C5", "R8C6", "R8C7", "R8C8", "R8C9", "R8C10"],
              ["R9C1", "R9C2", "R9C3", "R9C4", "R9C5", "R9C6", "R9C7", "R9C8", "R9C9", "R9C10"]]


Indexes[edit]

Similar to a one-dimensional array, every element in a two-dimensional array has a unique index. In addition, the syntax for retrieving and setting an element is the same. The only difference is that after the first index is passed, an array is returned. In order to access a particular element, two levels of indexes need to be used; one for the "outer" array and one for the "inner" array. For example, to retrieve the element at row 3 column 5, the indexes will be 2 and then 4 (because array indexing starts at zero):

twoDim[2][4] // R3C5

CoderMerlin™ Code Explorer: W0000 (1) 🟢


Loops[edit]

Similar to one-dimensional arrays, two-dimensional arrays are also commonly used within for loops. For example, to print each inner array on its own line, a single for loop can be used:

CoderMerlin™ Code Explorer: W0000 (2) 🟢


However, since each element of the outer array is itself an array, two levels of for loops are required to interact with each indivudal element. For example, in order to initialize twoDim with each row and column labelled, two for loops can be used to keep track of the row and then column:


CoderMerlin™ Code Explorer: W0000 (3) 🟢