Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin declare nested array

How can nested lists be declared in Kotlin? I'm looking for something in the form of:

var nestedList:List = [1,[2,[3,null,4]],[null],5]

so that I can flatten it later on (result should be nestedList = [1, 2, 3, 4, 5]).

like image 367
Dan Crisan Avatar asked Oct 23 '18 08:10

Dan Crisan


2 Answers

If you have nested arrays structure (for instance, val array: Array<Array<out Int?>> = arrayOf(arrayOf(1), arrayOf(2), arrayOf(3, null, 4))), you can just use flatten extension method:

println(array.flatten().filterNotNull())
like image 70
nyarian Avatar answered Nov 03 '22 04:11

nyarian


All common collections can't maintain variable layers count, so with them you can make only something like Andrey Ilyunin wrote - val array: Array<Array<out Int?>>.

But I wrote class structure to help you with your goal. It is no another collection and you can't work with it like it is, but it can make any layers amount you want. It is totally generic, so you can put there not only Int.

First of all, we start with NestedArrayItem class, which represents single item or one more nested array:

class NestedArrayItem<T> {
    private val array: ArrayList<NestedArrayItem<T>>?
    private val singleItem: T?
    constructor(array: ArrayList<NestedArrayItem<T>>) {
        this.array = array
        singleItem = null
    }
    constructor(singleItem: T?) {
        this.singleItem = singleItem
        array = null
    }
    fun asSequence(): Sequence<T?> =
        array?.asSequence()?.flatMap { it.asSequence() } ?:
            sequenceOf(singleItem)
    override fun toString() =
        array?.joinToString(prefix = "[", postfix = "]") ?:
            singleItem?.toString() ?: "null"
}

Then class NestedArray that is just like top level container for all the layers:

class NestedArray<T> {
    private val array: ArrayList<NestedArrayItem<T>> = arrayListOf()

    fun add(value: T?) {
        array.add(NestedArrayItem(value))
    }

    fun addNested(nestedArray: NestedArray<T>) {
        array.add(NestedArrayItem(nestedArray.array))
    }

    fun flatten(): ArrayList<T?> = array.asSequence()
        .flatMap { it.asSequence() }
        .toCollection(arrayListOf())

    override fun toString() = array.joinToString(prefix = "[", postfix = "]")
}

And to make it easier to write values I additionally wrote builder class for that:

class NestedArrayBuilder<T> private constructor(private val result: NestedArray<T>){
    constructor(fillNestedBuilder: NestedArrayBuilder<T>.() -> Unit) : this(NestedArray()) {
        NestedArrayBuilder(result).apply(fillNestedBuilder)
    }
    fun add(value: T?): NestedArrayBuilder<T> {
        result.add(value)
        return this
    }
    fun addArray(fillNestedBuilder: NestedArrayBuilder<T>.() -> Unit): NestedArrayBuilder<T> {
        val nestedResult = NestedArray<T>()
        val nestedArray = NestedArrayBuilder(nestedResult).apply(fillNestedBuilder)
            .build()
        result.addNested(nestedArray)
        return this
    }

    fun build() = result
}

That's it! You can use it. I put here example how to use it:

val array = NestedArrayBuilder<Int> {
    add(1)
    addArray {
        add(2)
        addArray {
            add(3)
            add(null)
            add(4)
        }
    }
    addArray {
        add(null)
    }
    add(5)
}.build()
assertEquals("[1, [2, [3, null, 4]], [null], 5]", array.toString())
assertEquals(arrayListOf(1, 2, 3, null, 4, null, 5), array.flatten())
like image 2
Ircover Avatar answered Nov 03 '22 04:11

Ircover