Most Efficient Way to Access Multi-Dimensional Arrays in Swift

Swift 2D Array Performance

If you really want to stick to a 2D array then you can use unsafe buffer pointers for faster access. However, 1D arrays are still going to be more efficient. Give this a shot.

    // 2D array assignment
myArray1.withUnsafeMutableBufferPointer { outer1 -> Void in
for i in 0..<numberOfItems {
outer1[i].withUnsafeMutableBufferPointer { inner1 -> Void in
for j in 0..<numberOfItems {
inner1[j] = x
x += 1
}
}
}
}

// 2D array access and assignment
myArray1.withUnsafeMutableBufferPointer { outer1 -> Void in
myArray2.withUnsafeMutableBufferPointer { outer2 -> Void in
for i in 0..<numberOfItems {
outer1[i].withUnsafeMutableBufferPointer { inner1 -> Void in
outer2[i].withUnsafeMutableBufferPointer { inner2 -> Void in
for j in 0..<numberOfItems {
inner2[j] = inner1[j]
}
}
}
}
}
}

What is the syntax for multidimensional array in Swift?

Yes, there is nothing built-in (as far as I know). You can define a custom class/struct (as in Most efficient way to access multi-dimensional arrays in Swift?
or How to Declare a Multidimensional Boolean array in Swift?) with a subscript operator, so that a[0,0] = 1 works.

Here is a mixture of those solutions, but as a
generic struct instead of class. I have also changed the order of
the rows and columns parameters because I find that more natural:

struct Array2D<T : IntegerLiteralConvertible > {
let rows : Int
let cols : Int
var matrix: [T]

init(rows : Int, cols : Int) {
self.rows = rows
self.cols = cols
matrix = Array(count : rows * cols, repeatedValue : 0)
}

subscript(row : Int, col : Int) -> T {
get { return matrix[cols * row + col] }
set { matrix[cols*row+col] = newValue }
}
}

I don't see how to create such a thing from a literal like
[2, 3 ;; -1, 0]. But you could initialize it from a nested array:

extension Array2D {

init(_ elements: [[T]]) {
let rows = elements.count
let cols = elements[0].count
self.init(rows: rows, cols: cols)
for i in 0 ..< rows {
assert(elements[i].count == cols, "Array must have same number of elements for each row")
self.matrix.replaceRange(cols * i ..< cols * (i+1), with: elements[i])
}
}
}

Example:

let array = Array2D([[1, 2, 3], [4, 5, 6]])
println(array.rows) // 2
println(array.cols) // 3
println(array[1, 2]) // 6
println(array[1, 0]) // 4

You can additionally implement the ArrayLiteralConvertible protocol to initialize a 2d array from a nested array literal:

extension Array2D : ArrayLiteralConvertible {

init(arrayLiteral elements: [T]...) {
self.init(elements)
}
}

Example:

let array : Array2D = [[1, 2, 3], [4, 5, 6]]

For square arrays (rows == columns) you could alternatively initialize it from a plain array:

extension Array2D {

init(_ elements: [T]) {
let rows = Int(sqrt(Double(elements.count)))
assert(rows * rows == elements.count, "Number of array elements must be a square")
self.init(rows: rows, cols: rows)
self.matrix = elements
}
}

Example:

let squareArray = Array2D([2, 3, -1, 0])
println(squareArray.rows) // 2
println(squareArray.cols) // 3
println(squareArray[1, 0]) // -1

Creating multi-dimensional arrays containing different types in Swift

You have stored tuple value inside array. so, access the contents of a tuple by providing the index position .

subPurchaseDate = itemList[0][1].0 // for string value
subPurchaseDate = itemList[0][1].1 // for date
subPurchaseDate = itemList[0][1].2 // for Double
subPurchaseDate = itemList[0][1].3 // for Int

You can also access using it's named value.

 var itemList: [[(stringValue: String, dateVal: Date, doubleValue: Double, intValue: Int)]] = []

// append elements in `itemList`
itemList.append([(stringValue: itemID, dateVal: purchase_date, doubleValue: purchase_date_ms, intValue: quantity)])

// access using it's named value

subPurchaseDate = itemList[0][1].stringValue // for string value
subPurchaseDate = itemList[0][1].dateVal // for date
subPurchaseDate = itemList[0][1].doubleValue // for Double
subPurchaseDate = itemList[0][1].intValue // for Int

Multidimensional arrays in Swift

For future readers, here is an elegant solution(5x5):

var matrix = [[Int]](repeating: [Int](repeating: 0, count: 5), count: 5)

and a dynamic approach:

var matrix = [[Int]]() // creates an empty matrix
var row = [Int]() // fill this row
matrix.append(row) // add this row

In Swift 5, how do I efficiently extract and use elements in multidimensional arrays?

You can create your own struct, something like this:

enum AmPm {
case am, pm
}
struct TimeMessage {
var ampm: AmPm
var hourStart: Int
var minuteStart: Int
var hourEnd: Int
var minuteEnd: Int
var message: String
}

With some convenience methods and properties:

extension TimeMessage {
init(_ ampm: AmPm, _ hourStart: Int, _ minuteStart: Int, _ hourEnd: Int, _ minuteEnd: Int, _ message: String) {
self.ampm = ampm
self.hourStart = hourStart
self.minuteStart = minuteStart
self.hourEnd = hourEnd
self.minuteEnd = minuteEnd
self.message = message
}
}

extension TimeMessage {
var hourStart24: Int {
switch ampm {
case .am:
return hourStart
case .pm:
return hourStart + 12
}
}

var hourEnd24: Int {
switch ampm {
case .am:
return hourEnd
case .pm:
return hourEnd + 12
}
}
}

extension TimeMessage {
func matches(_ date: Date) -> Bool {
let calendar = Calendar.current

let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)

return (hourStart24, minuteStart) <= (hour, minute)
&& (hour, minute) <= (hourEnd24, minuteEnd)
}
}

Using the above struct, you can declare your messageArray as:

let messageArray: [TimeMessage] = [
TimeMessage(.am, 00, 00, 00, 01, "it's \nmidnight"),
TimeMessage(.am, 00, 02, 02, 59, "it's very late (or early), \nto be up"),
TimeMessage(.am, 03, 00, 03, 01, "are you ready \nfor the 03 am call?"),
TimeMessage(.am, 03, 02, 03, 59, "go to sleep \ni'll let you know when it's \n4"),
//...
]

And use it as:

let now = Date()
if let timeMessage = messageArray.first(where: {$0.matches(now)}) {
print(timeMessage.message)
} else {
print("No match")
}

Populate a multidimensional array with a loop

Calling array[i][j] is for elements that are already there. You cannot use it to initialize the array, because currently it is just an empty array. You should be using .append instead. Keep in mind that this actually isn't a multi-dimensional array like Rob Napier states, but it accomplishes the same goal in this scenario. Try something like this:

var array = [[Int]]()
for i in 0...3 {

var subArray = [Int]()
for j in 0...3 {
subArray.append(i + j)
}

array.append(subArray)
}

This prints:

[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]

Again, may not be the best approach, but this is just how you could do it in Swift.

Two-dimensional array in Swift


Define mutable array

// 2 dimensional array of arrays of Ints 
var arr = [[Int]]()

OR:

// 2 dimensional array of arrays of Ints 
var arr: [[Int]] = []

OR if you need an array of predefined size (as mentioned by @0x7fffffff in comments):

// 2 dimensional array of arrays of Ints set to 0. Arrays size is 10x5
var arr = Array(count: 3, repeatedValue: Array(count: 2, repeatedValue: 0))

// ...and for Swift 3+:
var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)

Change element at position

arr[0][1] = 18

OR

let myVar = 18
arr[0][1] = myVar

Change sub array

arr[1] = [123, 456, 789] 

OR

arr[0] += 234

OR

arr[0] += [345, 678]

If you had 3x2 array of 0(zeros) before these changes, now you have:

[
[0, 0, 234, 345, 678], // 5 elements!
[123, 456, 789],
[0, 0]
]

So be aware that sub arrays are mutable and you can redefine initial array that represented matrix.

Examine size/bounds before access

let a = 0
let b = 1

if arr.count > a && arr[a].count > b {
println(arr[a][b])
}

Remarks:
Same markup rules for 3 and N dimensional arrays.



Related Topics



Leave a reply



Submit