Flatten array of arrays to one array in Kotlin - kotlin

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.

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

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>
}

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

.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

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

How did this code using zip() got the indexes of the elements in the list?

I'm solving exercises for a programming book in Kotlin. The task is to implement function using "zip()" and return a "List" of Pairs, where the first item in a "Pair" is the element, and the second item is the index of that element.
I solved the exercise, the solution works but I cannot understand the book solution.
Here is mine solution:
fun zipWithIndex(listToTake: List<Any>): List<Pair<Any, Any>> {
val finalList = mutableListOf<Any>()
var num = 0
for(element in listToTake) {
finalList += num
num ++
}
return (listToTake zip finalList)
}
fun main() {
val listToCall = listOf<String>("a", "b", "c")
println(zipWithIndex(listToCall))
}
And here is the book solution:
fun <T> List<T>.zipWithIndex(): List<Pair<T, Int>> =
zip(indices)
fun main() {
val list = listOf('a', 'b', 'c')
list.zipWithIndex() eq
"[(a, 0), (b, 1), (c, 2)]"
}
Can somebody please explain how does the book solution get the indexes of the elements in the list or tell me the topic that I need to read about to figure out how the code from the book works.
Thanks in advance for any help.
indices is a property of every kotlin List: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/#extension-properties
It's an IntRange of all valid indices, so essentially the range (https://kotlinlang.org/docs/ranges.html) equivalent of [0, 1, 2]. An IntRange is an Iterable, so it can be zipped with (the third zip overload in the api docs of list).
So it is equivalent to the zip you did, except you constructed [0, 1, 2] yourself while they used the pre-existing property of the List.
They also defined an extension function on List (https://kotlinlang.org/docs/extensions.html#extension-functions) instead of passing the list as a parameter.

How to initialize Kotlin IntArray from IntRange?

I'm trying to initialize an IntArray in Kotlin like so:
intArrayOf(1..9)
But I get a TypeError that Int is required, but I'm providing an IntRange. Is there a way to initialize the array with a range, or do I have to explicitly write out each value?
Using built in functions, this is how you could get to an IntArray from an IntRange:
val array: IntArray = (1..9).toList().toIntArray()
This is a bit wasteful, because it first constructs a list where it puts all the elements, and then it constructs an array as well. To do this directly, you could use your own extension, something like...
fun IntRange.toIntArray(): IntArray {
if (last < first)
return IntArray(0)
val result = IntArray(last - first + 1)
var index = 0
for (element in this)
result[index++] = element
return result
}
Which would give you this syntax:
val array: IntArray = (1..9).toIntArray()

How to print ClosedRange in Kotlin

I'm learning Kotlin , and I'm trying to undetstand Ranges
I created a range of String as follows
val alpha = "A".."Z"
I want to print this for that I wrote
for (item in alpha) println(item)
But it gives the error
Error:(13, 18) Kotlin: For-loop range must have an 'iterator()' method
Can anyone help, how to print this range?
Well you can't do it with Strings by default, since there's no iterator() for ClosedRange<String>, but Chars will work directly:
val r = 'A'..'Z'
r.forEach(::println)
It will be of type CharRange and provide the needed iterator().
To make your very special example work with Strings, you could define your own extension function and delegate to a Iterator<Char>:
operator fun ClosedRange<String>.iterator(): Iterator<String> {
val charIt = (start.toCharArray().first()..endInclusive.toCharArray().first()).iterator()
return object : Iterator<String> {
override fun hasNext() = charIt.hasNext()
override fun next(): String = charIt.nextChar().toString()
}
}
Now it works as you wished. But be aware that this does not make sense for most use cases with ranges of String.
val alpha = "A".."Z"
This is a plain range, which means it's an abstract representation of a contiguous subset within a total order. The only operation such an entity supports is answering the question "is this element within this range?", and it will be based purely on the contract of Comparable<T>.
In your case, consider a string like "THREAD". Does it belong to your range? It sorts higher than "A" but lower than "Z", so it does belong to it. But you probably didn't intend to iterate over it, or the infinity of all other strings belonging to your range.
What you considered as a given is actually a special case: iterable ranges. They are defined only on the three types representing integral types: IntRange, LongRange and CharRange. These are the types where the subset belonging to the range can actually be enumerated and iterated over.
I Loved The answers of #s1m0nw1 and #Marko Topolnik.
You can't really iterate over a ClosedRange<String>.
The literal may mislead us to think it's a ClosedRange<Char>
var range = "A".."C" // misleading literal
What you CAN do:
Just a small addition: is to map the characters to Strings
val r = ('A'..'Z').map { it.toString() }//.forEach(::println)
for (letter in r){
}
How to print this range?
I think the only way to print this range is
println(alpha)
And you'll get
A..Z
This is how to "print" this range.
You're trying to travel through a non-iterable range, this is invalid.
Like, you cannot for (i in File("a.txt")..File("Main.java")) println(i).
Can You Try This May Help You, I think you actually want 'A'..'Z' not "A".."Z"
var A = 'A'..'Z'
for(value in A){
println("$value")
}

Kotlin sequence concatenation

val seq1 = sequenceOf(1, 2, 3)
val seq2 = sequenceOf(5, 6, 7)
sequenceOf(seq1, seq2).flatten().forEach { ... }
That's how I'm doing sequence concatenation but I'm worrying that it's actually copying elements, whereas all I need is an iterator that uses elements from the iterables (seq1, seq2) I gave it.
Is there such a function?
Your code doesn't copy the sequence elements, and sequenceOf(seq1, seq2).flatten() actually does what you want: it generates a sequence that takes items first from seq1 and then, when seq1 finishes, from seq2.
Also, operator + is implemented in exactly this way, so you can just use it:
(seq1 + seq2).forEach { ... }
The source of the operator is as expected:
public operator fun <T> Sequence<T>.plus(elements: Sequence<T>): Sequence<T> {
return sequenceOf(this, elements).flatten()
}
You can take a look at the implementation of .flatten() in stdlib that uses FlatteningSequence, which actually switches over the original sequences' iterators. The implementation can change over time, but Sequence is intended to be as lazy as possible, so you can expect it to behave in a similar way.
Example:
val a = generateSequence(0) { it + 1 }
val b = sequenceOf(1, 2, 3)
(a + b).take(3).forEach { println(it) }
Here, copying the first sequence can never succeed since it's infinite, and iterating over (a + b) takes items one by one from a.
Note, however, that .flatten() is implemented in a different way for Iterable, and it does copy the elements. Find more about the differences between Iterable and Sequence here.
Something else you might need to do is create a sequence of sequences:
val xs = sequence {
yield(1)
yield(2)
}
val twoXs = sequence {
yieldAll(xs)
// ... interesting things here ...
yieldAll(xs)
}
This doesn't do anything that xs + xs doesn't do, but it gives you a place to do more complex things.

How can I tell Kotlin that an array or collection cannot contain nulls?

If I create an array, then fill it, Kotlin believes that there may be nulls in the array, and forces me to account for this
val strings = arrayOfNulls<String>(10000)
strings.fill("hello")
val upper = strings.map { it!!.toUpperCase() } // requires it!!
val lower = upper.map { it.toLowerCase() } // doesn't require !!
Creating a filled array doesn't have this problem
val strings = Array(10000, {"string"})
val upper = strings.map { it.toUpperCase() } // doesn't require !!
How can I tell the compiler that the result of strings.fill("hello") is an array of NonNull?
A rule of thumb: if in doubts, specify the types explicitly (there is a special refactoring for that):
val strings1: Array<String?> = arrayOfNulls<String>(10000)
val strings2: Array<String> = Array(10000, {"string"})
So you see that strings1 contains nullable items, while strings2 does not. That and only that determines how to work with these arrays:
// You can simply use nullability in you code:
strings2[0] = strings1[0]?.toUpperCase ?: "KOTLIN"
//Or you can ALWAYS cast the type, if you are confident:
val casted = strings1 as Array<String>
//But to be sure I'd transform the items of the array:
val asserted = strings1.map{it!!}
val defaults = strings1.map{it ?: "DEFAULT"}
Why the filled array works fine
The filled array infers the type of the array during the call from the lambda used as the second argument:
val strings = Array(10000, {"string"})
produces Array<String>
val strings = Array(10000, { it -> if (it % 2 == 0) "string" else null })
produces Array<String?>
Therefore changing the declaration to the left of the = that doesn't match the lambda does not do anything to help. If there is a conflict, there is an error.
How to make the arrayOfNulls work
For the arrayOfNulls problem, they type you specify to the call arrayOfNulls<String> is used in the function signature as generic type T and the function arrayOfNulls returns Array<T?> which means nullable. Nothing in your code changes that type. The fill method only sets values into the existing array.
To convert this nullable-element array to non-nullable-element list, use:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
Which is fine because your map call converts to a list anyway, so why not convert beforehand. Now depending on the size of the array this could be performant or not, the copy might be fast if in CPU cache. If it is large and no performant, you can make this lazy:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asSequence().filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
Or you can stay with arrays by doing a copy, but really this makes no sense because you undo it with the map:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings: Array<String> = Array(nullableStrings.size, { idx -> nullableStrings[idx]!! })
Arrays really are not that common in Java or Kotlin code (JetBrains studied the statistics) unless the code is doing really low level optimization. It could be better to use lists.
Given that you might end up with lists anyway, maybe start there too and give up the array.
val nullableStrings = listOf("a","b",null,"c",null,"d")
val strings = nullableStrings.filterNotNull()
But, if you can't stop the quest to use arrays, and really must cast one without a copy...
You can always write a function that does two things: First, check that all values are not null, and if so then return the array that is cast as not null. This is a bit hacky, but is safe only because the difference is nullability.
First, create an extension function on Array<T?>:
fun <T: Any> Array<T?>.asNotNull(): Array<T> {
if (this.any { it == null }) {
throw IllegalStateException("Cannot cast an array that contains null")
}
#Suppress("CAST_NEVER_SUCCEEDS")
return this as Array<T>
}
Then use this function new function to do the conversion (element checked as not null cast):
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asNotNull() // magic!
val upperStrings = strings.map { it.toUpperCase() } // no error
But I feel dirty even talking about this last option.
There is no way to tell this to the compiler. The type of the variable is determined when it is declared. In this case, the variable is declared as an array that can contain nulls.
The fill() method does not declare a new variable, it only modifies the contents of an existing one, so it cannot cause the variable type to change.