BBK#2: Find Max and Min

problemsolving Nov 18, 2020

Don't be a Java-ish dev in Kotlin-ish world, improve your knowledge about Koltin Standard Library and write better Kotlin code. ✌🏻 If your Java dev migrating to Kotlin this will help you learn alot!

Question: Print the Highest and Lowest number from the input string. "8 3 -5 42 -1 0 0 -9 4 7 4 -4"

Try it put yourself :
πŸ‘¨πŸ»β€πŸ’»πŸ‘‰ Β https://www.codewars.com/kata/554b4ac871d6813a03000035/train/kotlin



This solution requires accumulator pattern, read through my article on this it will help you out a lot!

Accumulator Pattern for beginners | Fold vs Reduce | AndroidBites
Accumulator pattern in kotlin | kotlin how to reduce | kotlin how to fold | reduce vs fold | best practice to reduce | transformation in kotlin | collection transformation | java vs koltin | java for loop anti patterns

Java-ish Solution:

Best Practice

  • Parsing to Int could fail, always use safe operations and provide fallbacks .i.e instead of Β toInt(), I have used toIntOrNull() and did ?: handling
  • Never propagate null from one scope to other, that is why ?: handling is important.
fun highAndLow(numbers: String): String {
    val numbs = numbers.split(" ")
    var max  = numbs.get(0).toIntOrNull() ?: -1
    var min  = numbs.get(0).toIntOrNull() ?: -1
    numbs.forEach { num ->
        val currNumb = num.toIntOrNull() ?: -1
        if(currNumb > max){
            max = currNumb
        } 
        
        if (currNumb < min){
            min = currNumb
        }
    }
    return "${max} ${min}"
}

Cons of this solutions:

  • Both max and min are mutable variable, and can be accidentally mutated anywhere within the scope of highAndLow() function
  • forEach iterations should be used only for reading values not for mutations, here it is used to external mutation of variables outside the scope of forEach loop. For such use cases transformation operators should be used example map, reduce etc.
  • Logic is over expressive

Solution 2:

Using fold
checkout -> https://chetangupta.net/reduce-fold/

fun highAndLow(numbers: String): String {

 val firstNumber: Int = numbers.split(" ").get(0).toIntOrNull() ?: -1
 val result = numbers.split(" ")
 	.fold(
    	Pair</*max*/Int,/*min*/Int>(firstNumber,firstNumber)
    ){ acc, it ->
        val item = it.toIntOrNull()?:-1
        return@fold when{
          item>acc.first -> acc.copy(first = item) // updateMax
          item<acc.second -> acc.copy(second = item) // updateMin
          else-> acc // do nothing..
      }
    }
    return "${result.first} ${result.second}"
}

Pros of this solutions:

  • We got rid of external mutation by getting rid of max,min variables
  • By using fold, we are reducing to the one result, mutations are scoped within the fold iteration.

Cons of this solutions:

  • Still bit more Expressive code, but not real deal breaker.

Solution 3:

Using stdlib functions of Max and Min
https://play.kotlinlang.org/byExample/05_Collections/13_max

Note: 'max(): T?' is deprecated. Use maxOrNull instead. 'min(): T?' is deprecated. Use minOrNull instead.
fun highAndLow(numbers: String): String {
  val nums = numbers.split(" ").map{it.toIntOrNull()}.filterNotNull()
  val max = nums.maxOrNull()?:-1
  val min = nums.minOrNull()?:-1
  return "$max $min"
}

Or If you have one-Liner fetish

fun highAndLow(numbers: String) = numbers.split(' ')
    .map(String::toIntOrNull)
    .filterNotNull()
    .let { "${it.max()} ${it.min()}" }

Puff...πŸ’¨ Extra expressive-ness is gone!.


Community Submission

Do check out various solution posted by the community pretty good devs write pretty badass code.

Contributed by holo

fun highAndLow(numbers:String):String{
    val seq = numbers.splitToSequence(' ')
    val first = seq.first().toIntOrNull()!!
    val result = seq.fold(Pair(first, first)) { acc, item ->
        val numb = item.toIntOrNull()
        return@fold if(numb!=null){
            acc.copy(first = maxOf(acc.first, numb), second = minOf(acc.second, numb))
        }else{
            acc
        }
    }
    return "${result.first} ${result.second}"
}

Pros

  • max/min functions iterates on the list twice (actually 3 times, split, minOrNull and maxOrNull). The fold method is much better, with only 2 iterations.
  • splitToSequence does not iterate on the string, it only generates a lazy sequence.
  • Notice that Holo used splitToSequence(' ') and not splitToSequence(" "), that is because char split is more efficient.
  • To make the solution even more efficient you should replace my acc.copy part with when expression like in solution 2. I'm leaving it like that to show that maxOf and minOf operations.

Contributed by ShahBaz

Pros

  • If you use mapNotNull you don't have to check for filterNotNull()

Contributed by theapache64

 import kotlin.system.measureTimeMillis
 
 measureTimeMillis {
        input
            .split(" ")
            .map { it.toInt() }
            .sorted()
            .let {
                println("Lowest : ${it.first()}")
                println("Highest : ${it.last()}")
            }
    }.let {
        println("Theapache64 solution took $it ms")
    }
}

Pros

  • Taught me how to measure the performance of the code, btw @theapache64 this approach is slower than mine πŸ˜‚.

Contributed by theDroidLady|Niharika

fun main() {
     println(getMinAndMax("1 2 13 41 5 hh -1 17 0 5 -1"))
}

private const val DEFAULT_MIN = -1
private const val DEFAULT_MAX = -1

fun getMinAndMax(numberString: String) : String =
       numberString
       	.split(' ')
       	.mapNotNull { stringItem -> stringItem.toIntOrNull() }
       	.distinct()
        .let { numbersList ->  "${numbersList.maxOrNull() ?: DEFAULT_MAX} ${numbersList.minOrNull() ?: DEFAULT_MIN}" }
}

Pros

  • Adding distinct make sure you are not working with same string twice
  • Very clean code πŸ₯‚ and battle-Tested.

Contributed by AbhishekBansal

Pros

Displaying malicious use of Regex + Functional reference.πŸ˜‚


Conclusion

Aim of these articles is not hate on Java, but to help people learn various ways in which they can write same logic better and more Kotlin standard library focused way.

Hope you find it informative and if you have any feedback or post request or want to subscribe to my mailing list forms are below.

Until next time. Happy Hacking! πŸ‘©β€πŸ’»

Solve Next :

BigBrainKotlin - Mumbling
Don’t be a Java-ish dev in Kotlin-ish world, improve your knowledge about Koltin Standard Library and write better Kotlin code. ✌🏻 If your Java dev migrating to Kotlin this will help you learn a lot!

Solve Previous:

BigBrainKotlin - Find Middle Element
Don’t be a Java-ish dev in Kotlin-ish world, improve your knowledge about Koltin Standard Library and write better Kotlin code. ✌🏻 If your Java dev migrating to Kotlin this will help you learn alot!

Enjoy the Post?

a clap is much appreciated if you enjoyed. No sign up or cost associated :)




Chetan gupta

Hi there! call me Ch8n, I'm a mobile technology enthusiast! love Android #kotlinAlltheWay, CodingMantra: #cleanCoder #TDD #SOLID #designpatterns

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.