Part of Github Copilot with Kotlin

Kotlin: Put May as the first in the list

Sandy LaneSandy Lane

Video: Kotlin: Put May as the first in the list by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Kotlin: Put "May" First in a List of Months

sortedWith(compareBy { ... }) plus a custom comparator key. The trick: assign 0 to "May" and 1 to everything else — stable sort puts May first while preserving the rest of the order.

A small list-reordering puzzle that demos how Kotlin's compareBy plus a one-line lambda can prioritise specific elements without writing a manual loop.

The task

Given ["January", "February", ..., "July"], produce a new list where "May" is first but the rest stay in their original order.

The Copilot prompt

fun main() {
  val months = listOf("January", "February", "March", "April", "May", "June", "July")

  // Move "May" to the front while keeping the order of other months

The comment is the spec. Copilot reads "move May to the front" and "keeping the order" — two constraints — and produces:

val reordered = months.sortedWith(compareBy { if (it == "May") 0 else 1 })
println(reordered)
// Output: [May, January, February, March, April, June, July]

Walkthrough

sortedWith(comparator) produces a new sorted list (the original is untouched). The comparator decides ordering.

compareBy { selector } is a Kotlin stdlib helper that builds a comparator from a "key" function. Each element is mapped to a key; the comparator sorts by those keys ascending.

The key here is if (it == "May") 0 else 1. So:

  • "May" maps to 0.
  • Every other month maps to 1.

When sorted ascending, 0 comes before 1 — May lands at the front. Among the 1-keyed elements, Kotlin's sort is stable: equal-keyed elements keep their original order. That's why "January, February, March, April, June, July" preserves their input order.

Without compareBy

The same logic spelled out:

val reordered = months.sortedWith(Comparator { a, b ->
  val keyA = if (a == "May") 0 else 1
  val keyB = if (b == "May") 0 else 1
  keyA.compareTo(keyB)
})

compareBy is sugar for "build this comparator from a key function." Always shorter.

A simpler approach

If you don't need the elegance of a comparator, a partition is more direct:

val (may, others) = months.partition { it == "May" }
val reordered = may + others

partition splits the list in two by a predicate — first part matches, second doesn't. + joins them. Same result.

For a single sentinel, partition + concat is arguably clearer than sortedWith. For prioritising multiple values (May first, June second, then the rest), compareBy scales better:

val priority = mapOf("May" to 0, "June" to 1)
val reordered = months.sortedWith(compareBy { priority[it] ?: 2 })

Refining the Copilot comment

If you write only "// reorder list to put May first," Copilot might produce:

val reordered = months.toMutableList()
reordered.remove("May")
reordered.add(0, "May")

Functional but mutates. To steer Copilot toward the immutable, idiomatic version:

"Move 'May' to the front while keeping the order of other months. Return a new list."

Mentioning "return a new list" nudges away from in-place mutation.

Stability matters

The reason this works at all is that Kotlin's sortedWith is stable — equal keys preserve insertion order. If it weren't, the 1-keyed months might come out in any order.

Collections.sort (Java) and sortedWith (Kotlin) both use Timsort, which is stable. Don't use unstable sorts (raw quicksort, hash-based) when relative order of equal elements matters.

Extension to "any priority list"

fun <T> List<T>.prioritize(target: T): List<T> =
  sortedWith(compareBy { if (it == target) 0 else 1 })

println(months.prioritize("May"))
println(months.prioritize("July"))

Generic, reusable. Add it to a Lists.kt file once, use everywhere.

Common mistakes

Forgetting that sortedWith returns a new list. months.sortedWith(...) doesn't change months. Many bugs come from "I sorted it but the list looks the same" — you have to assign the result.

Using sort (mutating) on a listOf. listOf(...) returns a List<T> (read-only). sort is on MutableList. Reach for sortedWith (immutable).

Comparator returning Boolean. compareBy { it == "May" } works in Kotlin (Boolean is Comparable, false < true), but the meaning is reversed — false (others) sorts before true (May), so May ends up last. Use the explicit 0/1 form to avoid the surprise.

Expecting alphabetical preservation. The "other months" come out in input order, not alphabetical. If you want "May first, then alphabetical," chain comparators:

months.sortedWith(
  compareBy<String> { if (it == "May") 0 else 1 }
    .thenBy { it }
)

thenBy { it } says "then by the element itself, alphabetically."

Common Copilot habits

When Copilot generates Kotlin list operations, it tends to reach for:

  • filter, map, sortedBy, groupBy — the functional staples.
  • toMutableList() + manual add/remove when the comment hints at "modify."
  • compareBy { ... } for any sort-with-criteria task.

Steering with comments shapes which style you get. "Return a new list" → functional. "Modify the list" → mutable.

What's next

Episode 3: Convert a string to a Date in Kotlin. Old-school SimpleDateFormat versus the modern LocalDate.parse — which one does Copilot reach for, and which should you keep?

Recap

sortedWith(compareBy { selector }) for custom-key sorting. Returns a new list — Kotlin's stable sort preserves input order among equal keys. Alternatives: partition + concat for single-sentinel priority; a Map of priorities for multi-tier. Steer Copilot away from mutations with "return a new list" in the comment. Comparator returning Boolean has reversed semantics — use 0/1.

Next episode: convert a string to a date.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.