Kotlin tips and tricks I learned from Advent of Code 2022/23

In 2022 I set out to complete advent of code each day using Kotlin. It was a lot of fun and following along with Jetbrains/Sebastians videos was a great way to explore alternative solutions. Along the way I learned and was reminded of the power of all the different tools we have available in Kotlin and the standard library which I’ve endeavoured to list below. You might learn something new or it might be a reminder of something forgotten. I’ll be checking over this list before next December to refresh.

in no particular order:

extension property

Extension functions are well known and loved but you can also have extension properties

val Card.nextUpgradeAttack: Int
    get() = (this.attackPoints * 1.2).toInt()Code language: Kotlin (kotlin)

Note that you need the getter as extension properties can’t have backing fields as explained in the Kotlin docs

destructuring

Create multiple variables from the individual components of something. Great for Pairs, Lists, Maps, and data classes.

val (part1, part2) = myString.split(" ,")

for ((key, value) in myMap) { println("$key, $value") }Code language: Kotlin (kotlin)

collection.chunked

Split a list into smaller lists of a given size (the last list may be smaller).

val words = "up 4 left 5 down 7 left 2".split(' ')
val moves = words.chunked(2)

// You can also transform the resulting chunks immediately
val moves = words.chunked(2) { (direction, distance) -> Move(direction, distance) }Code language: Kotlin (kotlin)

collection.map

Transform each item in a list

val numsList = listOf(1, 2, 3, 4, 5, 6)
val squares = numsList.map { it * it }
// 1, 4, 9, 16, 25, 36

val cards = listOf(Card(Jack, Diamond), Card(Four, Club))
val cardValues = cards.map { card -> card.value }Code language: Kotlin (kotlin)

downTo

Creates a range that counts down. Useful for loops. Includes start and end values

for (i in 5 downTo 0) { print(i) }
// 543210Code language: Kotlin (kotlin)

..< (now preferred to equivalent “until” function)

Create range excluding the end value

for (i in 1..<4) { print(i) }
// 123
for (j in 1..4) { print(j) }
// 1234Code language: Kotlin (kotlin)

step

Create a range with the given steps between values

for (i in 0..8 step 2) { print(i) }
// 02468
for (j in 8 downTo 0 step 2) print(j)
// 86420Code language: Kotlin (kotlin)

collection.lastIndex

When list.size doesn’t do the trick because it starts at zero, list.lastIndex steps in to save the day.

//skip the first value
for (i in 1..myList.lastIndex) { ... }

for (j in myList.lastIndex downTo 0) { ... }Code language: Kotlin (kotlin)

operator override

Create custom implementations of standard operators like +, *, in, etc.

data class Card(val attackPoints: Int) {
    operator fun plus(other: Card): Card {
        return Card(this.attackPoints + other.attackPoints)
    }
}

// Then you can do
val result = Card(7) + Card(4)Code language: Kotlin (kotlin)

The list of all the operators you can override is in the kotlin docs

infix

Add some syntactical flair that feels like you’re creating a new kotlin keyword

infix fun Map<Int, String>.excluding(other: Int): Map<Int, String> { ... }

// Normally a method call would look like this
myMap.excluding(5)
// but with infix it can be changed to
myMap excluding 5Code language: Kotlin (kotlin)

repeat(n)

Do the thing multiple times. Just shorthand for a 0 based for loop.

repeat(3) { index ->
    print("Hello$index")
}
// Hello0Hello1Hello2Code language: Kotlin (kotlin)

Can also repeat strings

println("Word".repeat(4)) 
// WordWordWordWordCode language: JavaScript (javascript)

collection.joinToString

Join elements in list together to form one string

val numbers = listOf(1, 2, 3, 4, 5, 6)
println(numbers.joinToString(
    prefix = "<",
    postfix = ">",
    separator = "•"
)) 
// <1•2•3•4•5•6>Code language: Kotlin (kotlin)

Supports transforming objects into strings to be joined

val cars = listOf(ferrari, lamborghini, ford, kia)
println(
    cars.joinToString(separator = ", ") { car ->
        "${car.brandName} is ${if (car.isCool) "cool" else "lame"}"
    }
)
// Ferrari is cool, Lamborghini is cool, Ford is lame, Kia is lame
        Code language: Kotlin (kotlin)

collection.reversed

Returns the list with elements in the reversed order

val numbers = listOf(1, 2, 3, 4, 5, 6)
val reversed = numbers.reversed()
println(reversed)
// [6, 5, 4, 3, 2, 1]Code language: Kotlin (kotlin)

collection.single()

Just a simplified way of checking there is only one element then getting that first element. Returns the single element or throws if there are less/more than one. Also .singleOrNull()

val one = listOf(1,2,3)
val two = listOf(1,6,11)
val commonElement = (one intersect two).single()
println(commonElement)
// 1Code language: Kotlin (kotlin)

collection.sum/sumOf

sum returns the sum of elements in a collection of numbers

val numbers = listOf(1,2,3,4,5)
println(numbers.sum())
// 15Code language: Kotlin (kotlin)

sumOf lets you transform elements to be counted

return inputLists.sumOf { list ->
    list.size
}Code language: Kotlin (kotlin)

collection.count

Finds the number of elements matching the predicate

val numbers = listOf(1,2,3,4,5)
println(numbers.count { num -> num % 2 == 0 })
// 2Code language: Kotlin (kotlin)

collection.all/any

All returns true of all elements match the given predicate. Any returns true if any element matches.

val numbers = listOf(1,2,3,4,5)
println(numbers.all { it % 2 == 0})
// false

println(numbers.any { it % 2 == 0})
// trueCode language: Kotlin (kotlin)

in

Check if element is in the collection

if (count in list) {
    print(“Count is present in the list”)
}Code language: Kotlin (kotlin)

Also used for loops

for (i in list) { ... }
// but probably you want to use list.forEach { ... }Code language: Kotlin (kotlin)

2023

Named loops

Allows you to break out of a loop early.

list.forEach name@ {
   if (...) {
       return@name
   }
   ...
}Code language: Kotlin (kotlin)

collection.windowed

Returns a list of lists captured by sliding a “window” across the list

val numbers = listOf(1,2,3,4,5,6,7,8)
val windows = numbers.windowed(size = 5, step = 1)
println(windows.toList()) 
// [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8]]

val numbers10 = listOf(1,2,3,4,5,6,7,8,9,10)
val partialWindows = numbers10.windowed(size = 5, step = 3, partialWindows = true)
println(partialWindows.toList()) 
// [[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10], [10]]Code language: Kotlin (kotlin)

String.findAnyOf/findLastAnyOf

Find the index of the first occurrence of any of the supplied strings

val input = "lluddlrlrrrduu"
val (firstIndex, foundString) = input.findAnyOf(listOf("u", "d"))!!
println("$foundString was found at index $firstIndex")
// u was found at index 2Code language: Kotlin (kotlin)

Number.coerceAtMost/Least/coerceIn

Similar to maxOf/minOf it ensures the returned value is within the constraints

val points = 73
println(points.coerceAtMost(70))
// 70
println(points.coerceAtLeast(80))
// 80
println(points.coerceIn(0,100))
// 73Code language: Kotlin (kotlin)

collection.partition

Split a list in two using the given condition

val list = listOf(Person("Tom", 18), Person("Andy", 32), Person("Sarah", 22))
val result = list.partition { it.age < 30 }
println(result) 
// ([Tom - 18, Sarah - 22], [Andy - 32])

val (greenApples, notGreenApples) = apples.partition { it.isGreen }Code language: Kotlin (kotlin)

collection.take/takeLast

Grab the first/last N elements from a list. Just a convenience method for .sublist(0,n)

println(listOf(1,2,3,4,5,6,7,8).take(3))
// 1,2,3
println(listOf(1,2,3,4,5,6,7,8).takeLast(3))
// 6,7,8Code language: Kotlin (kotlin)

collection.drop/dropLast

Get a sublist excluding the dropped elements

println(listOf(1,2,3,4,5,6,7,8).drop(3))
// 4,5,6,7,8
println(listOf(1,2,3,4,5,6,7,8).dropLast(3))
// 1,2,3,4,5Code language: Kotlin (kotlin)