Reputation: 3055
I get so confused about 2D arrays in Swift. Let me describe step by step. And would you please correct me if I am wrong.
First of all; declaration of an empty array:
class test{
var my2Darr = Int[][]()
}
Secondly fill the array. (such as my2Darr[i][j] = 0
where i, j are for-loop variables)
class test {
var my2Darr = Int[][]()
init() {
for(var i:Int=0;i<10;i++) {
for(var j:Int=0;j<10;j++) {
my2Darr[i][j]=18 /* Is this correct? */
}
}
}
}
And Lastly, Editing element of in array
class test {
var my2Darr = Int[][]()
init() {
.... //same as up code
}
func edit(number:Int,index:Int){
my2Darr[index][index] = number
// Is this correct? and What if index is bigger
// than i or j... Can we control that like
if (my2Darr[i][j] == nil) { ... } */
}
}
Upvotes: 140
Views: 263901
Reputation: 21830
It can sometimes help to make things simpler if you use a typealias
for the underlying array type. Here is an example for a 2D array of String
:
typealias Strings = [String]
let stringGroups: [Strings] // aka [[String]]
Upvotes: 1
Reputation: 4013
In Swift 5.5
It's just to understand how the 2D array can be mounted
import UIKit
var greeting = "Hello, playground"
var arrXY = [[String]]()
// 0 1 2 3
arrXY.append(["A","B","C","D"]) // 0
arrXY.append(["E","F","G","H"]) // 1
arrXY.append(["J","K","L","M"]) // 2
arrXY.append(["N","O","P","Q"]) // 3
print(arrXY)
print(arrXY[2][1]) // xy(2,1)
print(arrXY[0][3]) // xy(0,3)
print(arrXY[3][3]) // xy(3,3)
Upvotes: 5
Reputation: 11143
I would use a 1D array and just index it separately. For example consider using:
struct MatrixIndexer {
var rows: Int
var columns: Int
var count: Int {
return rows * columns
}
subscript(_ r: Int, _ c: Int) -> Int {
precondition(r < rows && c < columns)
return r * columns + c
}
func coordinate(_ i: Int) -> (row: Int, column: Int) {
precondition(i < count)
return (i / columns, i % columns)
}
}
let at = MatrixIndexer(rows: 16, columns: 8)
var array2D: [Float] = Array(repeating: 0, count: at.count)
array2D[at[3, 4]] = 3
array2D[at[5, 2]] = 6
(If you want a single struct to pass around, you could introduce an Array2D
which contains the MatrixIndexer
and an Array
).
Why is this a good idea? First, you get nicer operations out of the box such as:
Second, it is also much better for performance.
Upvotes: 1
Reputation: 1997
Swift version 5.5
This version uses an underlying array of optionals to avoid runtime surprises that would be caused by the use of assertions.
struct Matrix<T> {
let numberOfRows: Int
let numberOfColumns: Int
var elements: [T?]
/// Creates a new matrix whose size is defined by the specified number of rows and columns.
/// containing a single, repeated optional value.
///
/// - Parameters:
/// - repeatedValue: The element to repeat
/// - numberOfRows: The number of rows
/// - numberOfColumns: The number of columns
init(repeating repeatedValue: T? = nil, numberOfRows: Int, numberOfColumns: Int) {
// counts must be zero or greater.
let numberOfRows = numberOfRows > 0 ? numberOfRows : 0
let numberOfColumns = numberOfColumns > 0 ? numberOfColumns : 0
self.numberOfRows = numberOfRows
self.numberOfColumns = numberOfColumns
self.elements = Array(repeating: repeatedValue, count: numberOfRows * numberOfColumns)
}
subscript(row: Int, column: Int) -> T? {
get { elements[(row * numberOfColumns) + column] }
set { elements[(row * numberOfColumns) + column] = newValue }
}
}
Upvotes: 1
Reputation: 699
According to Apple documents for swift 4.1 you can use this struct so easily to create a 2D array:
Code sample:
struct Matrix {
let rows: Int, columns: Int
var grid: [Double]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(repeating: 0.0, count: rows * columns)
}
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
Upvotes: 14
Reputation: 439
This can be done in one simple line.
Swift 5
var my2DArray = (0..<4).map { _ in Array(0..<) }
You could also map it to instances of any class or struct of your choice
struct MyStructCouldBeAClass {
var x: Int
var y: Int
}
var my2DArray: [[MyStructCouldBeAClass]] = (0..<2).map { x in
Array(0..<2).map { MyStructCouldBeAClass(x: x, y: $0)}
}
Upvotes: 2
Reputation: 3605
Before using multidimensional arrays in Swift, consider their impact on performance. In my tests, the flattened array performed almost 2x better than the 2D version:
var table = [Int](repeating: 0, count: size * size)
let array = [Int](1...size)
for row in 0..<size {
for column in 0..<size {
let val = array[row] * array[column]
// assign
table[row * size + column] = val
}
}
Average execution time for filling up a 50x50 Array: 82.9ms
vs.
var table = [[Int]](repeating: [Int](repeating: 0, count: size), count: size)
let array = [Int](1...size)
for row in 0..<size {
for column in 0..<size {
// assign
table[row][column] = val
}
}
Average execution time for filling up a 50x50 2D Array: 135ms
Both algorithms are O(n^2), so the difference in execution times is caused by the way we initialize the table.
Finally, the worst you can do is using append()
to add new elements. That proved to be the slowest in my tests:
var table = [Int]()
let array = [Int](1...size)
for row in 0..<size {
for column in 0..<size {
table.append(val)
}
}
Average execution time for filling up a 50x50 Array using append(): 2.59s
Avoid multidimensional arrays and use access by index if execution speed matters. 1D arrays are more performant, but your code might be a bit harder to understand.
You can run the performance tests yourself after downloading the demo project from my GitHub repo: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground
Upvotes: 7
Reputation: 3053
Make it Generic Swift 4
struct Matrix<T> {
let rows: Int, columns: Int
var grid: [T]
init(rows: Int, columns: Int,defaultValue: T) {
self.rows = rows
self.columns = columns
grid = Array(repeating: defaultValue, count: rows * columns) as! [T]
}
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> T {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
var matrix:Matrix<Bool> = Matrix(rows: 1000, columns: 1000,defaultValue:false)
matrix[0,10] = true
print(matrix[0,10])
Upvotes: 29
Reputation: 2727
In Swift 4
var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]
Upvotes: 18
Reputation: 12220
// 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)
arr[0][1] = 18
OR
let myVar = 18
arr[0][1] = myVar
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.
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.
Upvotes: 284
Reputation: 2173
You should be careful when you're using Array(repeating: Array(repeating: {value}, count: 80), count: 24)
.
If the value is an object, which is initialized by MyClass()
, then they will use the same reference.
Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)
doesn't create a new instance of MyClass
in each array element. This method only creates MyClass
once and puts it into the array.
Here's a safe way to initialize a multidimensional array.
private var matrix: [[MyClass]] = MyClass.newMatrix()
private static func newMatrix() -> [[MyClass]] {
var matrix: [[MyClass]] = []
for i in 0...23 {
matrix.append( [] )
for _ in 0...79 {
matrix[i].append( MyClass() )
}
}
return matrix
}
Upvotes: 22
Reputation: 22956
From the docs:
You can create multidimensional arrays by nesting pairs of square brackets, where the name of the base type of the elements is contained in the innermost pair of square brackets. For example, you can create a three-dimensional array of integers using three sets of square brackets:
var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
When accessing the elements in a multidimensional array, the left-most subscript index refers to the element at that index in the outermost array. The next subscript index to the right refers to the element at that index in the array that’s nested one level in. And so on. This means that in the example above, array3D[0] refers to [[1, 2], [3, 4]], array3D[0][1] refers to [3, 4], and array3D[0][1][1] refers to the value 4.
Upvotes: 30