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)]