Given a maximum list size in parameter size and total amount of elements in parameter elements, I need to create a list of lists. What is the syntax for creating variables in for loops in Kotlin?
The way I'm thinking of going about this is to declare and create lists before elements are added to a list. Then, when a list has reached full capacity, it is switched out for the next list that is empty.
Here is the half-baked code:
fun listOfLists(size: Int, vararg elements: String): List<List<String>> {
var amountOfElements = elements.size
var currentSubList: List<String> = mutableListOf<String>()
val numberOfLists: Int = amountOfElements / size + 1
for (n in 0..numberOfLists) {
// Code for creating the total number of lists needed
}
for (e in elements) {
if (amountOfElements % size == 0) {
// Code for switching lists
}
amountOfElements--
}
As #dyukha correctly mentioned, what you need is chunked() function.
fun listOfLists(size: Int, vararg elements: String) =
elements.asList().chunked(size)
Or, if you want to be really efficient, you can also use asSequence():
fun listOfLists(size: Int, vararg elements: String) =
elements.asSequence().chunked(size)
chunked() doesn't work on Array, because it's defined on Iterable and Sequence, and Array doesn't implement any of them.
Related
I am trying to figure out the best way to map my sorted map to the arguments of another function. This is an example of what I have.
data class ValueDescription (val length: Int, val count: Int)
// Now I am trying to map to a variable that looks like this
// This variable cannot be changed, I have to return this variable in this format
// The output will be a list of ValueDescriptions with a length and count for each entry of the map I have
// I will add the results of myValues to a mutable list later
val myValues = ValueDescription(_length here__, __count here__)
I have a sorted map that I want to map to my values
// The map will look like this
// Where both Ints hold the lengths and counts
// For example I would have the length of 7 to count of 8
val out = Map<Int, Int>
How can I take the values in my sorted map and place them into the variable myValues?
I tried to map by looping through my map with the forEach method and doing something like
out.map{it.key to myValues.ValueDescription.length}
but this doesn't seem to work.
I'm not sure I completely understood the question. If I got it correctly, your input is the Map<Int, Int> and you want to transform it to a List<ValueDescription>.
You can definitely use the map function for this:
val inputMap: Map<Int, Int> = TODO("provide the initial map here")
val myValues = inputMap.map { (l, c) -> ValueDescription(l, c) }
The map function here iterates over the entries of the map, and transforms each of them into a value of type ValueDescription by calling our lambda (the part between braces { ... }).
Each entry of the map here contains a key (the length) and a value (the count). Instead of using it.key and it.value, you can also use parentheses like I did here with (l, c) to destructure the entry into its 2 parts and give them names like l and c. The above is equivalent to:
val myValues = inputMap.map { ValueDescription(it.key, it.value) }
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()
I have a question about Kotlin.
I tried two versions of Kotlin, 1.0.0 and 1.2.6.
Using Kotlin, we can initialize an array and access to its element like this.
val n: Int = 10
val arr = Array(n, { it } )
val i: Int = 0
println(arr[i])
However, I got an error with this code.
val n: Long = 10
val arr = Array(n, { it } )
val i: Long = 0
println(arr[i])
It seems that it is an only way to cast Long to Int in order to compile this code.
val n: Long = 10
val arr = Array(n.toInt(), { it } )
val i: Long = 0
println(arr[i.toInt()])
However, it seems too redundant to me, but I couldn't find any solutions. So my question is
Is there any way to initialize arrays and access elements with a Long
variable?
Does Kotlin have any reasons that Long variable should not be accepted here?
Kotlin comes with longArrayOf(1, 2, 3) which will create an array for you which contains Longs.
Note that what you are trying to do with println(arr[i]) is getting a Long value out of arr, but the indexing of arrays is done with Ints. It will never work with Longs:
/**
* Returns the array element at the given [index].
* This method can be called using the index operator.
*/
public operator fun get(index: Int): Long
If you want to initialize an array of longs of the given length, you can use the same top-level Array function:
val n = 10 // n is Int
val arrayOfLongs = Array(n) { it.toLong() } // Array of 10 longs
Here the number n is Int and the initializer function converts the integer index it of an element being initialized to Long, therefore we get an array of longs as the result.
Or you can use another similar function to create a specialized LongArray:
val longArray = LongArray(n) { it.toLong() } // LongArray of 10 longs
Both arrays store longs, but the latter does it more compactly.
In Kotlin documentation I found the following example:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
Is it possible (and how) to do the similar with 2D matrix:
for ((i, j, value) in matrix2D.withIndex()) {
// but iterate iver double index: i - row, j - column
if (otherMatrix2D[i, j] > value) doSomething()
}
How to make support this functionality in Kotlin class?
While the solutions proposed by miensol and hotkey are correct it would be the least efficient way to iterate a matrix. For instance, the solution of hotkey makes M * N allocations of Cell<T> plus M allocations of List<Cell<T>> and IntRange plus one allocation of List<List<Cell<T>>> and IntRange. Moreover lists resize when new cells are added so even more allocations happen. That's too much allocations for just iterating a matrix.
Iteration using an inline function
I would recommend you to implement a very similar and very effective at the same time extension function that will be similar to Array<T>.forEachIndexed. This solution doesn't do any allocations at all and as efficient as writing nested for cycles.
inline fun <T> Matrix<T>.forEachIndexed(callback: (Int, Int, T) -> Unit) {
for (i in 0..cols - 1) {
for (j in 0..rows - 1) {
callback(i, j, this[i, j])
}
}
}
You can call this function in the following way:
matrix.forEachIndexed { i, j, value ->
if (otherMatrix[i, j] > value) doSomething()
}
Iteration using a destructive declaration
If you want to use a traditional for-loop with destructive declaration for some reason there exist a way more efficient but hacky solution. It uses a sequence instead of allocating multiple lists and creates only a single instance of Cell, but the Cell itself is mutable.
data class Cell<T>(var i: Int, var j: Int, var value: T)
fun <T> Matrix<T>.withIndex(): Sequence<Cell<T>> {
val cell = Cell(0, 0, this[0, 0])
return generateSequence(cell) { cell ->
cell.j += 1
if (cell.j >= rows) {
cell.j = 0
cell.i += 1
if (cell.i >= cols) {
return#generateSequence null
}
}
cell.value = this[cell.i, cell.j]
cell
}
}
And you can use this function to iterate a matrix in a for-loop:
for ((i, j, item) in matrix.withIndex()) {
if (otherMatrix[i, j] > value) doSomething()
}
This solution is lightly less efficient than the first one and not so robust because of a mutable Cell, so I would really recommend you to use the first one.
These two language features are used for implementing the behaviour that you want:
For-loops can be used with any class that has a method that provides an iterator.
for (item in myItems) { ... }
This code will compile if myItems has function iterator() returning something with functions hasNext(): Boolean and next().
Usually it is an Iterable<SomeType> implementation (some collection), but you can add iterator() method to an existing class as an extension, and you will be able to use that class in for-loops as well.
For destructuring declaration, the item type should have componentN() functions.
val (x, y, z) = item
Here the compiler expects item to have component1(), component2() and component3() functions. You can also use data classes, they have these functions generated.
Destructuring in for-loop works in a similar way: the type that the iterator's next() returns must have componentN() functions.
Example implementation (not pretending to be best at performance, see below):
Class with destructuring support:
class Cell<T>(val i: Int, val j: Int, val item: T) {
operator fun component1() = i
operator fun component2() = j
operator fun component3() = item
}
Or using data class:
data class Cell<T>(val i: Int, val j: Int, val item: T)
Function that returns List<Cell<T>> (written as an extension, but can also be a member function):
fun <T> Matrix<T>.withIndex() =
(0 .. height - 1).flatMap { i ->
(0 .. width - 1). map { j ->
Cell(i, j, this[i, j])
}
}
The usage:
for ((i, j, item) in matrix2d.withIndex()) { ... }
UPD Solution offered by Michael actually performs better (run this test, the difference is about 2x to 3x), so it's more suitable for performance critical code.
The following method:
data class Matrix2DValue<T>(val x: Int, val y: Int, val value: T)
fun withIndex(): Iterable<Matrix2DValue<T>> {
//build the list of values
}
Would allow you to write for as:
for ((x, y, value) in matrix2d.withIndex()) {
println("value: $value, x: $x, y: $y")
}
Bear in mind though that the order in which you declare data class properties defines the values of (x, y, value) - as opposed to for variable names. You can find more information about destructuring in the Kotlin documentation.
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.