Home > Back-end >  Flatten array of arrays to one array in Kotlin
Flatten array of arrays to one array in Kotlin

Time:02-15

I have the following Java 11 code (the contents of arr1 and arr2 are not so simple in my code, and I have more than 2 arrays, but the concept is the same):

String[] arr1 = new String[] {"a","b"};
String[] arr2 = new String[] {"c", "d"};
var req = Stream.of(arr1, arr2).flatMap(Stream::of).toArray(String[]::new);

The purpose of this code is to take all the values in multiple arrays of Strings and produce a single String array out. It needs to be an array, not a collection, due to an API outside my control accepting a String array later in the code.

In this simple example, the resulting array should have the following elements in this order: { "a", "b", "c", "d" }.

What is the canonical way to flatten a 1-deep array of arrays into a single array in Kotlin?

The main reason I'm being thrown for a loop here is that the IntelliJ Java to Kotlin converter did a pretty bad job of converting this code, leaving it with multiple weird syntax errors in the output Kotlin. The rest of my code that doesn't use things like method references converted much more cleanly to Kotlin.

CodePudding user response:

I don't know about canonical, but perhaps the simplest equivalent is:

val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
val req = arrayOf(arr1, arr2).flatten().toTypedArray()

That creates an Array<String> with the four values you want.

Here we're not transforming the values, simply repackaging them, so flatten() is simpler than the more common flatMap.

(It's normally better to use lists and other collections — the standard library has much better support for them, as well as avoiding issues around generics and type erasure — but having to interoperate with an old or badly-designed API, as specified in this question, is one of the corner cases you may still need arrays for, along with varargs and low-level collection implementation.)

CodePudding user response:

The easiest in Kotlin would be:

val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
val result = arrayOf(arr1, arr2)
    .flatten()
    .toTypedArray()

flatten() creates a List and toTypedArray() converts it to an array. This could be considered a waste of CPU cycles, but on the other hand, I don't see a way to create an array from a lazy stream/sequence directly, because we don't know the resulting size. So my guess would be that Java's Stream.toArray() also copies the data several times in the process (?).

If we need to limit copying to the minimum, we can create our own extension:

inline fun <reified T> Array<out Array<out T>>.flattenToArray(): Array<T> {
    val result = arrayOfNulls<T>(sumOf { it.size })
    var pos = 0
    for (arr in this) {
        arr.copyInto(result, pos)
        pos  = arr.size
    }
    @Suppress("UNCHECKED_CAST")
    return result as Array<T>
}

CodePudding user response:

If you literally just want to put them in a single array, you can use the spread operator

val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
val result = arrayOf(*arr1, *arr2)

I'm not sure there's a way to do that for an arbitrary number of source arrays, so if you need that, then flatten is the way to go. This is quick and easy for unpacking a specific bunch of arrays though

CodePudding user response:

.flatten() should do the job.

val arr1 = arrayOf("a", "b")
val arr2 = arrayOf("c", "d")
arrayOf(arr1, arr2).flatten()

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/flatten.html

CodePudding user response:

flatten() is the simple way to do it. All the standard array and Iterable operators return Lists. If you actually need an array, you can call toTypedArray() on the returned List.

You can start by combining them into a List or Array.

val arr1 = arrayOf(“a”, “b”)
val arr2 = arrayOf(“c”, “d”)
val req = listOf(arr1, arr2).flatten() //.toTypedArray()
  • Related