I've been learning Kotlin lately and I came across something I can't understand.
import kotlin.random.Random
fun main() {
var maximumDiscountValue = 0
repeat(3) { index ->
val discount = Random.nextInt(10)
println("Attempt ${index+1}: $discount")
if (discount > maximumDiscountValue) {
maximumDiscountValue = discount
}
}
println(maximumDiscountValue)
}
val number = 3
var output = 2
repeat(5) { index ->
output += (index * number)
}
println(output)
I don't understand what "index" does in there. If someone knows what, I'll be glad to know.
The repeat function specifies a number of times (3 and 5 in your case) a specific lambada function has to be executed. Index is a zero-based number that will be incremented during each execution.
In your first repeat
repeat(5) {
... your index will be 0, 1, 2, 3 and 4
}
Here the source code of the repeat function
#kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
Related
I'm trying to generate a list with several values that add up to the total value. The list works properly, but I still want to ask if there are possibilities to optimize this better. I have tried several attempts with different functionalities but unfortunately this does not give the exact result.
I don't need the exact solution just a step in the right direction will help me a lot.
This is how the code looks right now.
fun getTotal(amps:List<Int>): MutableMap<Int, Int>{
val listEqual = mutableMapOf<Int,Int>()
if(amps.size > 8) {
listEqual[0] = listOf(
amps[0],
amps[1],
amps[2],
amps[3],
amps[4],
amps[5],
amps[6],
amps[7],
amps[8],
amps[9]
).sum()
if(amps.size > 18) {
listEqual[1] = listOf(
amps[10],
amps[11],
amps[12],
amps[13],
amps[14],
amps[15],
amps[16],
amps[17],
amps[18],
amps[19]
).sum()
if(amps.size > 28) {
listEqual[2] = listOf(
amps[20],
amps[21],
amps[22],
amps[23],
amps[24],
amps[25],
amps[26],
amps[27],
amps[28],
amps[29]
).sum()
if(amps.size > 38) {
listEqual[3] = listOf(
amps[30],
amps[31],
amps[32],
amps[33],
amps[34],
amps[35],
amps[36],
amps[37],
amps[38],
amps[39]
).sum()
if(amps.size > 47) {
listEqual[4] = listOf(
amps[40],
amps[41],
amps[42],
amps[43],
amps[44],
amps[45],
amps[46],
amps[47],
amps[48]
).sum()
}
}
}
}
}
return listEqual
}
fun getTotal(amps: List<Int>): Map<Int, Int> {
return amps
.windowed(10, 10, true)
.mapIndexed { index: Int, ints: List<Int> -> index to ints.sum() }
.toMap()
}
The part with windowed() looks likes this with named arguments:
.windowed(size = 10, step = 10, partialWindows = true)
It 'slides' over a list by step and returns size elements. If partialWindows is set to true, the last part, if less than size, will be returned too, if set to false, it will be omitted.
The sizes you are checking seem arbitrary. Like if the input size is 9, it would crash at amps[9]. You should be checking >= multiples of 10 in your if statement since you are grouping chunks of 10.
Also, there doesn't seem much point in using a Map where the keys are consecutive Ints starting from 0. It might as well be a List. If you can return a List, your whole function could be:
return amps.chunked(10, Iterable<Int>::sum)
If you do need a Map, you can do
return amps.chunked(10, Iterable<Int>::sum)
.mapIndexed { i, list -> i to list }
.toMap()
If you need the option of dropping the last group if it's smaller than 10, then the other answer with windowed is more appropriate.
If chunked and windowed were not available, your code could be shortened using the subList() function and a loop. For example:
fun getTotal(amps:List<Int>): MutableMap<Int, Int>{
val listEqual = mutableMapOf<Int,Int>()
repeat(5) { chunkIndex ->
if (amps.size >= chunkIndex * 10 + 10) {
listEqual[chunkIndex] = amps.subList(chunkIndex * 10, chunkIndex * 10 + 10).sum()
}
}
return listEqual
}
String example (not necessary A and B, but not X): AAAXBBXBXBBB
I want to find all occoruncaes of X and save them in an array.
I wrote a function:
val arrayList = ArrayList<Int>()
fun findAllX(str: String){
for(i in 0 until str.length){
if(str[i] == 'X'){
arrayList.add(i)
}
}
}
Result in this example: arrayList that contains integers 3, 6, 8
Is there a better/cleaner way to write this?
Here is a one-liner:
"AAAXBBXBXBBB".withIndex().filter { it.value == 'X' }.map { it.index }.toTypedArray()
Info
I have an Item class file as follows:
class Item(var color:String, var numValue:Int, var drawableID:Int){
init {
color = this.color
numValue = this.numValue
drawableID = this.drawableID
}
}
In the main code I create an array which contains 104 objects by default attributes:
var myItemClassArray = Array(104) { Item("", -99, -99) }
Also I have pictures in my drawable folder and I have their IDs in an array which is drawablesIDs:Array<Int>, and it contains 53 elements.
Problem
I want to assign my Item attributes as in this picture: https://i.stack.imgur.com/wFVsn.png I can do it for a similar problem (which has 106 objects and 53 drawables) with the code given in below:
for (i in 0 until 106) {
if (i < 13) {
myItemClassList[i+2].color = "kirmizi"
myItemClassList[i+2].numValue = i+1
myItemClassList[i+2].drawableID = drawablesIDs[i+1]
} else if (i in 13..25) {
myItemClassList[i+2].color = "siyah"
myItemClassList[i+2].numValue = (i+1)-13
myItemClassList[i+2].drawableID = drawablesIDs[i+1]
} else if (i in 26..38) {
myItemClassList[i+2].color = "yesil"
myItemClassList[i+2].numValue = (i+1)-26
myItemClassList[i+2].drawableID = drawablesIDs[i+1]
} else if (i in 39..51) {
myItemClassList[i+2].color = "mavi"
myItemClassList[i+2].numValue = (i+1)-39
myItemClassList[i+2].drawableID = drawablesIDs[i+1]
} else if (i in 52..64) {
myItemClassList[i+2].color = "kirmizi"
myItemClassList[i+2].numValue = (i+1)-52
myItemClassList[i+2].drawableID = drawablesIDs[(i+1)-52]
} else if (i in 65..77) {
myItemClassList[i+2].color = "siyah"
myItemClassList[i+2].numValue = (i+1)-65
myItemClassList[i+2].drawableID = drawablesIDs[i+1-65+13]
} else if (i in 78..90) {
myItemClassList[i+2].color = "yesil"
myItemClassList[i+2].numValue = (i+1)-78
myItemClassList[i+2].drawableID = drawablesIDs[i+1-78+26]
} else if (i in 91..103) {
myItemClassList[i+2].color = "mavi"
myItemClassList[i+2].numValue = (i+1)-91
myItemClassList[i+2].drawableID = drawablesIDs[i+1-91+39]
} else {
myItemClassList[0].color = "sahte"
myItemClassList[0].drawableID = drawablesIDs[0]
myItemClassList[1].color = "sahte"
myItemClassList[1].drawableID = drawablesIDs[0]
}
}
Is there a cleaner way to do this?
One can use lambda expression to create an array. For example:
val test = Array(28){i-> examples[i]}
This works fine with one "i" parameter. But if I want to try something like this:
val test = Array(28){if(i<13)-> examples[i]}
it gives me an error because of it's syntax is wrong.
More Simple Question
Let's say we have an array which has numbers from 0 to 28 like this:
val testNumbers= Array(28){i->i}
Now I want to create an array which will contain numbers from 0 to 10 using lambda.
How do I this:
val player6 = Array(10){(it<10) -> testNumbers[it]} // gives an syntax error
From what I could gather from your picture, I've made these three assumptions:
The numValue is grouped in groups of 13 items
Each group receives a color in the order: kirmizi -> siyah -> yesil -> mavi, then it cycles again
The drawable IDs cycles every 52 items
Based on this, I came up with the following solution:
data class Item(var color: String = "", var numValue: Int = -99, var drawableId: Int = -99)
fun main() {
val colors = listOf("kirmizi", "siyah", "yesil", "mavi")
val drawableIDs = (0..52).toList() // This is just a stub. in your case it will be your drawable lists
val edgeCase = arrayListOf(Item("sahte", drawableId = drawableIDs[0]), Item("sahte", drawableId = drawableIDs[0]))
val pattern = (0 until 104)
.map { index -> Pair(index, index / 13) }
.map { (index, group) ->
Item(
color = colors[group % 4],
numValue = index+1,
drawableId = drawableIDs[(index % 52) + 1]
)
}
val total = pattern + edgeCase
total.forEach { println(it) }
}
You can play around with it on this kotlin playground.
Is there a cleaner way to do this?
From what I gather, you want to initialize only the first 13 values of a contiguous array with 28 spaces, leaving the rest with either their default values or null.
The reason why your code doesn't work is because the Array initializer expects you to return an object. the if block by itself is not an expression in kotlin, so it doesn't evaluate to a value, so you need to provide an else branch for it to work.
val examples = Array(28) { if (i < 13) examples[i] else defaultExample }
This is stated in the Kotlin documentation for control flow:
If you're using if as an expression rather than a statement (for example, returning its value or assigning it to a variable), the expression is required to have an else branch.
More simple question
In this case you could just use take:
// If you don't want to modify it
val player6 = testNumbers.take(10)
.toTypedArray() // Since take returns a List, you need to turn it back into an array
// If you want to modify the items
val player6 = testNumbers.take(10)
.map { value -> modifyNumber(value) }
.toTypedArray()
Tip: In kotlin if you declare your constructor parameter with val or var they are already attributes from your class and you don't need to initialize manually in the init block.
/*
* You don't need to do this:
* class Item(var color: String, var numValue: Int, var drawableId: Int) {
* init {
* this.color = color
* this.numValue = numValue
* this.drawableId = drawableId
* }
* }
*/
// Kotlin already does it for you
class Item(var color: String, var numValue: Int, var drawableId: Int)
fun main() {
val myitem = Item("blue", 20, 100)
println(myitem.color)
println(myitem.numValue)
println(myitem.drawableId)
}
Here is a possible solution :
fun getColor(i: Int) = when (i) {
in 0..1 -> "sahte"
in 2..13, in 52..64 -> "kirmizi"
in 65..77, in 13..25 -> "siyah"
in 26..38, in 78..90 -> "yesil"
in 39..51, in 91..103 -> "mavi"
else -> ""
}
fun getNumValue(i: Int) = when (i) {
in 0..1 -> -99
in 2..13 -> i - 1
in 13..25 -> (i - 1) - 13
in 26..38 -> (i - 1) - 26
in 39..51 -> (i - 1) - 39
in 52..64 -> (i - 1) - 52
in 65..77 -> (i - 1) - 65
in 78..90 -> (i - 1) - 78
in 91..103 -> (i - 1) - 91
else -> -99
}
fun getDrawableID(i: Int) = when (i) {
in 0..1 -> drawablesIDs[0]
in 2..13, in 13..25, in 26..38, in 39..51 -> drawablesIDs[i - 1]
in 52..64 -> drawablesIDs[(i - 1) - 52]
in 65..77 -> drawablesIDs[i - 1 - 65 + 13]
in 78..90 -> drawablesIDs[i - 1 - 78 + 26]
in 91..103 -> drawablesIDs[i - 1 - 91 + 39]
else -> -99
}
val myItemClassArray = Array(104) {
Item(getColor(it), getNumValue(it), getDrawableID(it))
}
Maybe there is some mistakes in the different ranges.
The main advantages are :
each mapping is testable independently
no mutability
The problem here return#Foreach not getting executed it always return false
on the if statement
fun check() {
var x: Int? = null
val numbers = 1..100
numbers.forEach {
x = it
if (it == 2) {
return#forEach
}
}
showingText("$x Hello World")
}
Actually, the it==2 condition is true when it is 2, and the statement return#forEach executed then. You can see it with simple logs:
fun check() {
var x: Int? = null
val numbers = 1..100
numbers.forEach {
println(it)
x = it
if (it == 2) {
println("FINISH")
return#forEach
}
}
println("$x Hello World")
}
So, the return is executed and stops the forEach method. Then the sequence of range moves to the next item (3, 4, and so on) and action is run for it consequently.
If you want to stop actions after 2 found, use simple loop:
fun check2() {
var x: Int? = null
val numbers = 1..100
for(it in numbers) {
println(it)
x = it
if (it == 2) {
println("FINISH")
break
}
}
println("$x Hello World")
}
I tried to use this tutorial youtube tutorial. I have a function as follows:
fun fact(x:Int):Int{
tailrec fun factTail(y:Int, z:Int):Int{
return if(y == 0) {
z
} else {
factTail(y - 1, y * z)
}
}
return factTail(x,1)
}
and this function is called in oncreate as:
var abc = fact(5)
Log.i(TAG, "5! = $abc")
When the app outputs log it shows like this:
I/MainActivity: 5! = 0
Can anyone point out what is wrong here.
You code is right and you definitely get 0 for multiple result more than MAX_SIZE of Int value. You can get Int max size with:
Int.MAX_VALUE
So if this y * x cross Int.MAX_VALUE = 2147483647, fun will return 0 to you.
For number bigger than 16 func will return minus number and for greater than 33 it will return 0. you can check this by:
for(x in 5..50){
log.i("$x! : ${fact(x)}")
}
So you can handle this by changing variable from Int to Long
fun fact(x : Long) : Long {
fun factTail(y : Long , z :Long):Long {
return if (y == 0L) z
else return factTail(y-1 ,y*z)
}
return factTail(x ,1)
}
But Long also have its limitation. Hope you get the point.