Kotlin: Idiomatic & Declarative way to find count of a loop without mutation

How to count no.of times a loop has run with a condition, for example:

var count = 0
while (stk.isNotEmpty()) {
  stack.pop()
  count++
}

But this involves a temporary counter and mutation. Is there a better idiomatic and declarative way without using any temp variable and mutation. I could come-up with something like this, but it does feel hacky:

val count = stk.takeWhile { stk.isNotEmpty() }.also { stk.pop() }.count()
1 Like

I would go for something like:

fun main() {
val stk = listOf(1, 2, 5, 70)

//sampleStart
stk.forEachIndexed { index, value -> 
    println("The value at index $index is $value")
}
//sampleEnd
}
1 Like
fun main() {
    val stk = listOf(1, 2, 5, 70)

//sampleStart
    val count = stk.onEach { println(it) }.count()
//sampleEnd
    
    println("Count=$count")
}

What about this

fun main() {
//sampleStart
val stk = listOf(1, 2, 5, 70)
val count = stk.fold(0) { acc, value -> 
    println("Iterated $value")
    acc+1
}
println("Count $count")
//sampleEnd
}
1 Like

Your code is simple, easy to understand, performant and safe. There is no need to dogmatically complicate things for some ideal/rule/…

The solutions given above:

  • count(): Move the mutable state to another function. So it is not visible, but works the same way.
  • fold(...): Adds overhead for the lookup and/or creation of primitive wrappers (at least on the JVM).

Mutable shared state is evil. Immutability is evil, as you can see. Really, you need to find some place where you have mutable state that’s not shared.

From The Trouble with Memory by Kirk Pepperdine, which explains why you should allocate as few objects as possible.

Your code has mutable state that is not shared. Perfect :slight_smile:

7 Likes

I tend to fold. Is there better way when counter is conditional?

val enabledTotalCount = fold(0) { total, item ->
...
if (enable) {
 val wasEnabled = f()
 total + if (wasEnabled) 1 else 0
} else { total }

In case the total value of the counter is irrelevant to the lambda, i would suggest using sumBy instead of fold.

val count = stk.fold(0) { acc, value -> 
    println("Iterated $value")
    acc+1
}

could be

val count = stk.sumBy {
    println("Iterated $it")
    1
}

or in case of @pasn1ak

val count = elements.sumBy {
    ...
    if (enable && f()) 1 else 0
}
2 Likes