Wednesday, 10 April 2019

Using Multi-Dimensional Arrays in Swift 5


Coming from old school programming, I sort of grew to regard multi dimensional arrays as a given; the stuff one learns at the second programming lesson during one’s early high school career. These days, that does not seem to be the case anymore, to the point of finding myself wasting way more time than I thought I’d need in order to figure how to work it out in Swift 5. This post is therefore here in order to help me solidify my findings, and if - in the process - I’d actually end up helping others, then I have done even better.

First, I will point you to the sources I have found the most helpful. That honour falls unto Paul Hudson for this post, to which I will add I have been finding his posts (and for that matter, his books) very helpful.
My second source of inspiration was this post Multidimensional Arrays in Swift from iAchieved.it, which provides some good practical examples.
Feel free to pause and have a look at these two before reading the rest of this post.

On to the main event.
The problem I was facing, which can be generally summed up as “how do I use multi-dimensional arrays in Swift 5”, can be further broken down into the following:
  1. How do I declare a multi dimensional array in Swift 5?
  2. Once declared, how do I even address a particular member in the array’s matrix?
  3. How do I add values into such an array?
  4. In particular, how do all of the above happen when my array is not of a simple type (say, Int or String), but is rather a multi dimensional array of a complex struct that is made its own arrays, booleans, and other complex structures?
  5. How do I manipulate particular (and generally unknown) cells in the array’s matrix, while leaving most of the other cells alone? Specifically, how do I get to do that in Swift, with its prudish (but justifiable) emphasis on declaring and initialising anything and everything?

With that in mind, let’s have a detailed look at the various solutions and compromises I was able to identify. I’m sure experts will have a look and then laugh, but - regardless - this is what I was able to come up with within a reasonable timeframe.
The declaration part is easy. This is how one defines a two dimension array of a struct that I called Cell:
var grid = Cell()

Once declared, accessing a particular “cell” in the matrix is done via:
grid[i][j]
Where i and j point at the row and column in that array.

Obviously, before accessing grid[i][j] there have to be values there (or Xcode will raise a runtime error), which brings us to the more tricky bits.
I have found that simply asking Xcode to add individual “cells”, say grid[0][0], would not work. I had to append rows to the matrix first!
For example, in a case where I needed a matrix of 10 rows, I needed to do something like this in order to get rid of runtime errors later -
for _ in 0…9 {
            grid.append([Cell(value: 0, providedByUser: false)])
        }
This adds 10 rows and 1 column into my grid, all of which contain data that I could - for now - ignore.
What is worth noting here, however, is the syntax I had to apply in order to append my values. As mentioned, individual cells in my matrix are made of a struct that is more complex than, say, Int; in this particular case, they are made of value (which is an Int) and of providedByUser (which is a boolean).

With this initialisation of the grid now performed, I was finally able to enter the individual values I wanted into specific cells of my grid. However, as per usual Swift standards, I had to do it properly and in order, so I ended up doing it using for loops:
for i in 0…9 {
            for j in 0…9 {
                if j == 0 {
                    grid[i][j] = (Cell(value: 1, providedByUser: true))
                } else {
                    grid[i].append(Cell(value: 1, providedByUser: true))
                }
            }
        }
In the above, do note the different way of setting values when dealing with the first column of a row as opposed to when dealing with the rest of the row. That difference is a side effect of the fact I had already created that first column when I declared my 10 rows earlier with the minimum I could get away with - a single column.
Obviously, this very issue indicates at more elegant ways in which a multidimensional array could be set up. The point, if there was one, is to point out the importance of initialising rows in our multidimensional arrays and point a finger at ways to do so.

Now, if I want to print the values of my grid, that is how I do it:
for i in 0…9 {
            for j in 0…9 {
                print(“\(i) \(j) \(grid[i][j])”)
            }
        }

I cannot claim to be ecstatic about the way I manipulated the array in order to get the result I wanted, but I did end up with a working multidimensional (or rather, two dimensional) array. I guess if it was all lovely and simple, there would not have been a need for this post…
One question I still don’t have an answer for is, how do I achieve everything I had achieved here in a case where I do not know the size of my matrix in advance. I can think of ways around it, but they all require some form of non elegant manoeuvring that is likely to get frowned upon by the purists. Purists whose feedback and inputs I’d love to have, BTW.