Count sum of all list elements until condition is satisfied - kotlin

I faced the following problem: I have a list of objects. Let it be objects of the class Test:
data class Test(
var status: String, // Can be EXPIRED, WAIT
var amount: Float
)
The array is sorted, there are objects with the status EXPIRED in the beginning and after objects with the status WAIT located. I need to calculate the sum of all elements with the status EXPIRED (if they exist) and add to this sum amount of the first object with the type WAIT (if it exists). Now I have the following code:
private fun getRestructuringAmountToPay(): Float {
var i = 0
var sum = 0F
list?.forEachIndexed { iter, el ->
if (el.status != RestructingItemStatus.WAIT) {
i = iter
sum += el.amount ?: 0F
}
}
if (i + 1 < (list?.size ?: 0)) {
sum += list?.get(i+1)?.amount ?: 0F
}
return sum
}
But is there any way to improve this code and make it Kotlin-pretty? Thanks in advance for any help!

since your list is sorted and EXPIRED items are first you can use firstOrNull
to find the first item with status == WAIT
while you iterate over EXPIRED items you can use a simple variable to sum the amount and when you found the first WAIT item just assign the sum to amount
var sum: Float = 0f
list.firstOrNull {
sum += it.amount
it.status == "WAIT"
}?.apply {
this.amount = sum
}

I would go old school and use a for loop rather than the forEachIndexed method so that I can break out of the loop when I hit the first WAIT entry. I'd do something like this:
private fun getRestructuringAmountToPay(): Float {
var sum = 0F
for (el in list) {
if (el.status == RestructingItemStatus.WAIT) {
el.amount += sum
break
}
else {
sum += el.amount
}
}
return sum
}
This is a simple and elegant way to do the bare minimum amount of work without any extra iterations over the list. If you're one of the "I have to cram everything into as few lines of code as possible" crowd, there are certainly more sophisticated and compact ways to go about this. I often struggle to understand the actual advantage of such solutions.

When you say you want to find all of the elements in the list with the status "EXPIRED", that makes me think of filter(). When you say you want to sum them, that makes me think of sumBy(). And when you say you want to add that number to the first element in the list with the status "WAIT", that makes me think of first().
We can't actually use the normal sumBy() function because Test.amount is of type Float, so the closest we can do is use sumByDouble() and convert amount to a Double.
val expiredSum = list.filter { it.status == "EXPIRED" }.sumByDouble { it.amount.toDouble() }
val result = list.first { it.status == "WAIT" }.amount + expiredSum
If you don't want to throw an exception if there are no elements with the status "WAIT", use firstOrNull() instead:
val expiredSum = list.filter { it.status == "EXPIRED" }.sumByDouble { it.amount.toDouble() }
val initialValue = list.firstOrNull { it.status == "WAIT" }?.amount ?: 0F
val result = initialValue + expiredSum

Actually, your code is not doing what you want
calculate the sum of all elements with the status EXPIRED (if they exist) and add to this sum amount of the first object with the type WAIT (if it exists)
It calculates the sum of all elements with the status EXPIRED (if they exist) and add to this sum amount of the first object with the type WAIT located after the last object with EXPIRED status (if it exists) OR amount of the object with index one (if it exist) if there were no elements with status EXPIRED:
println(getRestructuringAmountToPay(listOf(Test("EXPIRED", 1f), Test("WAIT", 1f), Test("EXPIRED", 1f)))) //will print 2.0, while following original description it should be 3.0
println(getRestructuringAmountToPay(listOf(Test("WAIT", 1f), Test("WAIT", 100f)))) //Will print 100.0, while following original description it should be 1.0
To get originally desired behavior in Kotlin-way you need to do the following:
if (list == null) return 0f //get rid of nullability
val (expired, waiting) = list.partition { it.status != "WAIT" } //split original list into respectful partitions
val sum = expired.map { it.amount }.sum() + (waiting.firstOrNull()?.amount ?: 0f) //do the calculations

I'am not sure I understand your solution but respectively to your goal this:
var sum = list.filter { it.status == "EXPIRED" }.sumByDouble { it.amount.toDouble() }
list.firstOrNull{ it.status == "WAIT" }?.let { sum+=it.amount}
println(sum)

Pretty is subjective - the code below is short but admittedly does not take advantage of the "sorted" nature of the collection
fun sum(data: List<Test>): Double {
val expiredSum = data.filter { it.status == "EXPIRED" }.sumByDouble { it.amount }
val waitSum = data.find { it.status == "WAIT" }?.amount ?: 0.0
return expiredSum + waitSum
}

Related

How do I write this for-loop in functional style?

I have a function with a for-loop:
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
var sum = 0
for (item in this) {
if (sumFunction(item))
sum += item
}
return sum
}
I want to know how I can write the above in functional style. I know that I have to use this.reduce(), but don't know exactly how to implement it.
return filter(sumFunction).sum()
Should be self-explanatory.
You can’t use reduce because it doesn’t let you reject the first element.
With fold it would be:
return fold(0) { a, b ->
if(sumFunction(b)) a + b else a
}
I can think if two ways to achieve that:
The first one is by using sumOf {...}:
.
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
return sumOf {
if (sumFunction(it)) it else 0
}
}
The second one is by using filter {...} then sum():
.
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
return filter(sumFunction).sum()
}
return this.reduce { sum, n -> if (sumFunction(n)) sum + n else 0}
If you really want to use reduce for some reason you can - but you need to add that 0 to the head of the list as your "start state":
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
val stuff = listOf(0) + this
return stuff.reduce { a, b -> a + if (sumFunction(b)) b else 0 }
}
You have to do that because reduce is really there to combine a bunch of items, which is why for the first iteration you get the first two items in the list. You don't get to handle them separately, which is why you need to throw that 0 in there to get past that first step, and get to a point where you can just do your checking on the second parameter and ignore the first one, treating it as an accumulator instead of another item you also need to check.
That behaviour is what fold is for - with that function you pass in an initial state (which can be a completely different type from your items, since you're not just smushing them together to create a new value like with reduce) and then on each iteration you get that state and an item.
You can handle the item as you like, and then make changes to the accumulator state depending on the result. Which is exactly the behaviour of your for loop! fold is just a functional way to write one. Tenfour04's answer is how you'd do it - it's the right tool for the job here!

Write a kotlin program that prints the number that is repeated the most in a consecutive way

I'm kind of stuck, I don't know how to make the second loop to start 1 position above the first loop in Kotlin.
I have an array (named myArray) with 10 elements, I need to Write a Kotlin program that prints the number that has the most consecutive repeated number in the array and also prints the number of times it appears in the sequence.
The program must parse the array from left to right so that if two numbers meet the condition, the one that appears first from left to right will be printed.
Longest: 3
Number: 8
fun main() {
val myArray: IntArray = intArrayOf(1,2,2,4,5,6,7,8,8,8)
for((index , value) in myArray.withIndex()){
var inx = index + 1
var count = 0
var longest = 0
var number = 0
for((inx,element) in myArray.withIndex()) {
if(value == element ){
count+=
}
}
if(longest < count){
longest = count
number = value
}
}
}
I'm against just dropping answers, but it is quite late for me, so I'll leave this answer here and edit it tomorrow with more info on how each part works. I hope that maybe in the meanwhile it will help you to gain some idea to where you might be going wrong.
val results = mutableMapOf<Int, Int>()
(0..myArray.size - 2).forEach { index ->
val current = myArray[index]
if (current == myArray[index + 1]) {
results[current] = (results[current] ?: 1) + 1
}
}
val (max, occurrences) = results.maxByOrNull { it.value } ?: run { println("No multiple occurrences"); return }
println("Most common consecutive number $max, with $occurrences occurrences")
Alternatively if the intArray would be a list, or if we allowed to change it to a list myArray.toList(), you could replace the whole forEach loop with a zipWithNext. But I'm pretty sure that this is a HW question, so I doubt this is the expected way of solving it.
myList.zipWithNext { a, b ->
if (a == b) results[a] = (results[a] ?: 1) + 1
}

How do we know which element is minimum in loop kotlin

Hey I have list in kotlin. I am iterating over it. I want to store true/false as flag index which one is minimum value in whole list. I tried some code, but it's not working.
fun main() {
val list = mutableListOf(4.83, 4.39, 3.58, 3.50, 3.46)
val minValue = mutableListOf<BestMinimumValue>()
var previousValue = 0.0
list.forEach {
minValue.add(BestMinimumValue(compareValue = previousValue > it))
previousValue = it
}
minValue.forEach {
println(it)
}
}
data class BestMinimumValue(
val compareValue: Boolean
)
Actual Output
BestMinimumValue(compareValue=false)
BestMinimumValue(compareValue=true)
BestMinimumValue(compareValue=true)
BestMinimumValue(compareValue=true)
BestMinimumValue(compareValue=true)
I'll explain what I need. In my list 3.46 is minimum value so on that place I need the flag as true and other one will be false.
Expected Output
BestMinimumValue(compareValue=false)
BestMinimumValue(compareValue=false)
BestMinimumValue(compareValue=false)
BestMinimumValue(compareValue=false)
BestMinimumValue(compareValue=true)
It is not possible to create the resulting list while iterating, because we can't know if the current item is the smallest one or not. We have to iterate at least twice: first searching for the minimum value and then creating a resulting list.
One solution is to find the minimum and then compare items to it:
val min = list.minOrNull()!!
val minValue = list.map { BestMinimumValue(it == min) }
Note if there are multiple same minimum values, then all of them will be marked as true.
If this is not expected behavior or if we like to avoid comparing items twice, then we can find the index of the minimum value and just construct the resulting list with it:
val minIndex = list.withIndex().minByOrNull { it.value }!!.index
val minValue = List(list.size) { BestMinimumValue(it == minIndex) }

How to avoid !! in a function which returns a non-nullable

In the sample below, the function should return a non-null data.
Since the data could be changed in the process, it needs to be var, and can only be nullable to start with.
I can't use lateinit because the first call of if (d == null) will throw.
After the process it will be assigned a non-null data, but the return has to use the !! (double bang or non-null assertion operator).
What is the best approach to avoid the !!?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
If you don't like !! then supply a default value for it. You'll realize you can only supply the default value if the list is not empty, but, as you said, the list is already known to be non-empty. The good part of this story is that the type system doesn't track list size so when you say dataArray[0], it will take your word for it.
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
Normally, you can and should lean on the compiler to infer nullability. This is not always possible, and in the contrived example if the inner loop runs but once d is non-null. This is guaranteed to happen if dataArray has at least one member.
Using this knowledge you could refactor the code slightly using require to check the arguments (for at least one member of the array) and checkNotNull to assert the state of the dataArray as a post-condition.
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
Remember you can return the result of a checkNotNull (and similar operators):
val checkedD = checkNotNull(d)
See Google Guava's Preconditions for something similar.
Even if you were to convert it to an Option, you would still have to deal with the case when dataArray is empty and so the value returned is undefined.
If you wanted to make this a complete function instead of throwing an exception, you can return an Option<Data> instead of a Data so that the case of an empty dataArray would return a None and leave it up to the caller to deal with how to handle the sad path.
How to do the same check, and cover the empty case
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
This uses the ?: operator to either get the minimum, or if the minimum is null (the list is empty) throws an error instead.
The accepted answer is completly fine but just to mentioned another way to solve your problem by changing one line in your code: return d ?: dataArray[0]

Error in Print prime number using high order functions in kotlin

val listNumbers = generateSequence(1) { it + 1 }
val listNumber1to100 = listNumbers.takeWhile { it < 100 }
val secNum:Unit = listNumber1to100.forEach {it}
println(listNumber1to100.asSequence().filter { it%(listNumber1to100.forEach { it })!=0 }.toList())
I have an error in reminder sign!
This is Error: None of the following functions can be called with the arguments supplied
In your first approach, the error appears in this line:
it%(listNumber1to100.forEach { it })
A Byte, Double, Float, Int, Long or Short is prefered right after the % operator, however, forEach is a function which the return type is Unit.
In your second approach, you have the correct expression in isPrime(Int). Here are some suggestions for you:
listNumber1to100 is excluding 100 in your code, if you want to include 100 in listNumber1to100, the lambda you pass to takeWhile should be changed like this:
val listNumber1to100 = listNumbers.takeWhile { it <= 100 }
listNumber1to100.asSequence() is redundant here since listNumber1too100 is itself a TakeWhileSequence which implements Sequence.
isPrime(Int) is a bit confusing since it is check for isComposite and it does not work for every input it takes(it works for 1 to 99 only). I will rewrite it in this way:
fun isPrime(num: Int): Boolean = if (num <= 1) false else !(2..num/2).any { num % it == 0 }
Since prime number must be positive and 1 is a special case(neither a prime nor composite number), it just return false if the input is smaller or equal to 1. If not, it checks if the input is divisible by a range of number from 2 to (input/2). The range ends before (input/2) is because if it is true for num % (num/2) == 0, it is also true for num % 2 == 0, vise versa. Finally, I add a ! operator before that because a prime number should not be divisible by any of those numbers.
Finally, you can filter a list by isPrime(Int) like this:
println(listNumber1to100.filter(::isPrime).toList())
PS. It is just for reference and there must be a better implementation than this.
To answer your question about it, it represents the only lambda parameter inside a lambda expression. It is always used for function literal which has only one parameter.
The error is because the expression: listNumber1to100.forEach { it } - is not a number, it is a Unit (ref).
The compiler try to match the modulo operator to the given function signatures, e.g.: mod(Byte) / mod(Int) / mod(Long) - etc.
val listNumbers = generateSequence(1) { it + 1 }
val listNumber1to100 = listNumbers.takeWhile { it < 100 }
fun isPrime(num: Int): Boolean = listNumber1to100.asSequence().any { num%it==0 && it!=num && it!=1 }
println(listNumber1to100.asSequence().filter { !isPrime(it)}.toList())
I found this solution and worked
But why can I have a non-number here in the right side of reminder