7

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

4
  • 1
    What is the data type that you expect this structure to have? Kotlin is a statically typed language, so you can either have an array (or list) where each element is also an array (or list), or an array (or list) where each element is a value. Commented Oct 23, 2018 at 8:32
  • so array or list?:) Commented Oct 23, 2018 at 8:37
  • Considering the problem I'm trying to solve, I would need an array where each element is also an array. Commented Oct 23, 2018 at 8:38
  • actually in this particular example Int and Array types are mixed. a single array contains either 1 and another array: [1,[2,[3,null,4]],...] Commented Oct 23, 2018 at 8:38

2 Answers 2

2

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())
Sign up to request clarification or add additional context in comments.

4 Comments

This is what I was looking for, but is there a way so that I do not have to hard code the number of layers (in your example that being 2)?
pls man dont try at home and at work. inline fun <reified T> flatArray(array: Array<*>): Array<T> { var temp: Array<*> = arrayOf(*array) while (!temp.isEmpty() && temp[0] is Array<*>) { temp = flatLevel(temp) } val result: Array<T?> = arrayOfNulls(temp.size) for ((index, value) in temp.withIndex()) { result[index] = value as T } return result.asSequence().filterNot { it.isNull() }.toList().toTypedArray() as Array<T> }
fun flatLevel(array: Array<*>): Array<*> { if (!array.isEmpty()) { val result = mutableListOf<Any?>() for (innerArray in array) { val castedArray = innerArray as Array<*> for (innerItem in castedArray) { result.add(innerItem) } } return result.toTypedArray() } return array }
I just did it because it was challenging. it is BETTER to find another solution which just won't result into this code. the best thing possible is to forbid variable deepness and use type-safe code
2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.