Home > Blockchain >  Flatten 2D list with indices
Flatten 2D list with indices

Time:12-09

I am looking for an idiomatic method in Kotlin (JVM) to flatten a 2-dimensional list. But, unlike the existing .flatten(), I would like to retain the individual indices, similar to .withIndex(), but 2-dimensional.

Example:

// Before
[
  ["a", "b", "c"],
  ["d", "e"],
  ["f", "g", "h", "i"]
]

// Afterwards, something like
[
  (0, 0, "a"), (0, 1, "b"), (0, 2, "c"),
  (1, 0, "d"), (1, 1, "e"),
  (2, 0, "f"), (2, 1, "g"), (2, 2, "h"), (2, 3, "i")
]

CodePudding user response:

flattenWithIndex

Unsure whether there is an idiomatic approach to this already somewhere in the lib, but it is fairly easy to write this yourself with a bunch of nested .withIndex() and some (flat)-mapping:

// inspired by IndexedValue for 1D
data class GridValue<out T>(val rowIndex: Int, val colIndex: Int, val value: T)

// similar to flatten().withIndex() for 1D
fun <T> Iterable<Iterable<T>>.flattenWithIndex(): List<GridValue<T>> =
  withIndex().flatMap { (rowIndex, row) ->
    row.withIndex().map { (colIndex, value) ->
      GridValue(rowIndex, colIndex, value)
    }
  }

withGridIndex

As a little bonus, here is the 2D variant of withIndex():

// only difference is map vs flatMap
fun <T> Iterable<Iterable<T>>.withGridIndex(): List<List<GridValue<T>>> =
  withIndex().map { (rowIndex, row) ->
    row.withIndex().map { (colIndex, value) ->
      GridValue(rowIndex, colIndex, value)
    }
  }

Notes

As usual, substitute with sequences if necessary.

Technically, flattenWithIndex() could also be rewritten as

fun <T> Iterable<Iterable<T>>.flattenWithIndex(): List<GridValue<T>> =
  withGridIndex().flatten()

But that would create unecessary lists in the process.

CodePudding user response:

As you describe question is actually do type conversion like: List<List<String>> to List<Triple<Int, Int, String>>

Generalization: List<List<T>> to List<Triple<Int, Int, T>> and code is:

fun <T> List<List<T>>.flattenWithIndex(): List<Triple<Int, Int, T>> {
    val result = mutableListOf<Triple<Int, Int, T>>()
    forEachIndexed { i, tList ->
        tList.forEachIndexed { j, t ->
            result.add(Triple(i, j, t))
        }
    }
    return result
}

val list = listOf(listOf("a", "b", "c"), listOf("d", "e"), listOf("f", "g", "h", "i"))
println(list.flattenWithIndex())// [(0, 0, a), (0, 1, b), (0, 2, c), (1, 0, d), (1, 1, e), (2, 0, f), (2, 1, g), (2, 2, h), (2, 3, i)]
  • Related