J.E.Tkaczyk
J.E.Tkaczyk

Reputation: 587

Navigating multidimensional arrays with nested for-loops in Kotlin

Quite often I am navigating a two-dimensional image matrix of pixels or a three-dimensional volumetric data set. Typically the underlying data structure is a one-dimensional array of some primitive type like Double or Int. The multi-dimensional nature is navigated through by nested for-loops as illustrated below.

Given that Kotlin does not currently support the typically C and Java-like for-loop structure, there seems some lost flexibility. I'm wondering if there is a clever iterator or Kotlin syntax to make this more elegant.

In Java below, we have the ability to capture the whole iteration concisely by embedding the master index i into the for-loops and only involving implicit increment operations which are presumably computationally efficient (compared to division and modulo)...

public static void main(String[] args) {
    int nrow=1000, ncol=1000;
    int size=nrow*ncol;
    double[] data = new double[size];

    for (int i=0, r = 0; r < nrow; r++) {
        for (int c = 0; c < ncol; c++,i++) {
            data[i]= r*c;
        }
    }
}

In Kotlin as follows, I find a solution where the index i is forced to have scope outside the loop block. Also the i++ line is somewhat buried and separated from the loop structure. I'm missing in this situation the elegant Kotlin syntax such as 'builders', no semi-colons, etc. I humbly submit that this is because the for-loop flow-control structure is less expressive in Kotlin. Admittedly, this is not critical, but more of a disappointment.

fun main(args : Array<String>) {
    val nrow = 1000
    val ncol = 1000
    val size = nrow * ncol
    val data = DoubleArray(size)

    var i = 0
    for(r in 0 until nrow) {
        for(c in 0 until ncol) {
            data[i] = (r * c).toDouble()
            i++
        }
    }
}

Upvotes: 2

Views: 6124

Answers (3)

J.E.Tkaczyk
J.E.Tkaczyk

Reputation: 587

Here is the answer I mentioned involving division and modulo

fun main(args : Array<String>) {
    val nrow = 1000
    val ncol = 1000
    val size = nrow * ncol
    val data=DoubleArray(size,{(it/ncol * it%ncol).toDouble()})
}

Upvotes: 0

jrtapsell
jrtapsell

Reputation: 7001

The simple way

You can calculate the index based on the offsets...

fun main(args : Array<String>) {
    val nrow = 1000
    val ncol = 1000
    val size = nrow * ncol
    val data = DoubleArray(size)

    for(r in 0 until nrow) {
        for(c in 0 until ncol) {
            data[(ncol*r) + c] = (r * c).toDouble()
        }
    }
}

The wrapper way

You can wrap the array, simplifying access...

class ArrayWrapper<T>(val height: Int, val width: Int, val default: Int) {
    private val data: Array<Any?> = Array(height, {default})
    operator fun get(x: Int, y: Int) = data[(width * y) + x] as T
    operator fun set(x: Int, y: Int, value: T) {
        data[(width * y) + x] = value
    }

    val rowIndices = (0 until width)
    val columnIndices = (0 until height)
}

fun main(args : Array<String>) {
    val nrow = 1000
    val ncol = 1000
    val data = ArrayWrapper<Double>(nrow, ncol, 0)

    for(r in data.rowIndices) {
        for(c in data.columnIndices) {
            data[r, c] = (r * c).toDouble()
        }
    }
}

Upvotes: 4

Stefan Golubović
Stefan Golubović

Reputation: 1275

This is one way of creating your array in Kotlin, without using i (or for loops):

val data = (0 until nrow).flatMap { r ->
    (0 until ncol).map { c ->
        (r * c).toDouble()
    }
}.toDoubleArray()

Upvotes: 2

Related Questions