greenoldman
greenoldman

Reputation: 21082

What is the syntax for multidimensional array in Swift?

CO fans: before you jump into conclusion it is a duplicate, there are 2 distinct flavours of arrays, and it seems I am asking about less popular one.

So far, I saw only syntax for jagged arrays like [[Int]], but I couldn't find any info about flat multidimensional arrays. So what is the syntax for type (like 2d array of Ints) and what is the syntax of initializing such array with data? Is there any [2, 3 ;; -1, 0]?

Upvotes: 0

Views: 2046

Answers (2)

Martin R
Martin R

Reputation: 540145

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

Upvotes: 4

rintaro
rintaro

Reputation: 51911

An example implementation supporting arbitrary number of dimensions:

struct ArrayMultiDimension<T> {
    private var _base:[T]
    let _dimensions: [Int]

    init(initialValue: T, dimensions:Int...) {
        _base = Array(count: reduce(dimensions, 1, *), repeatedValue: initialValue)
        _dimensions =  dimensions
    }
    private func _position2idx(position:[Int]) -> Int {
        assert(position.count == _dimensions.count)
        return reduce(Zip2(_dimensions, position), 0) {
            assert($1.0 > $1.1)
            return $0 * $1.0 + $1.1
        }
    }
    subscript(position:Int...) -> T {
        get { return _base[_position2idx(position)] }
        set { _base[_position2idx(position)] = newValue }
    }
}

// Usage:

var array3d = ArrayMultiDimension(initialValue: "", dimensions: 4,3,2)

for x in 0 ..< 4 {
    for y in 0 ..< 3 {
        for z in 0 ..< 2 {
            array3d[x,y,z] = "\(x)-\(y)-\(z)"
        }
    }
}

array3d[1,2,0] = "foo"

But, this could be very slow...

Upvotes: 0

Related Questions