Part of Github Copilot with Kotlin

Copilot Kotlin: Convert a list of numbers to string

Sandy LaneSandy Lane

Video: Copilot Kotlin: Convert a list of numbers to string by Taught by Celeste AI - AI Coding Coach

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

Kotlin: Convert a List of Numbers to a Comma-Separated String

numbers.joinToString(", "). One method, exactly the output you want. The Kotlin alternative to String.join (Java) or ", ".join(...) (Python).

A small puzzle: take [1, 2, 3, 4, 5] and produce "1, 2, 3, 4, 5". Kotlin's joinToString does it cleanly with a single optional separator argument.

The Copilot prompt

fun main() {
  val numbers = listOf(1, 2, 3, 4, 5)
  // Convert the list to a comma-separated string

Copilot generates:

val result = numbers.joinToString(separator = ", ")
println(result)   // "1, 2, 3, 4, 5"

joinToString signature

fun <T> Iterable<T>.joinToString(
  separator: CharSequence = ", ",
  prefix: CharSequence = "",
  postfix: CharSequence = "",
  limit: Int = -1,
  truncated: CharSequence = "...",
  transform: ((T) -> CharSequence)? = null,
): String

Six parameters, all optional with sensible defaults. Use them:

listOf(1, 2, 3).joinToString()
// "1, 2, 3"   (default separator is ", ")

listOf(1, 2, 3).joinToString(separator = " | ")
// "1 | 2 | 3"

listOf(1, 2, 3).joinToString(prefix = "[", postfix = "]")
// "[1, 2, 3]"

listOf(1, 2, 3).joinToString(separator = ", ", prefix = "[", postfix = "]")
// "[1, 2, 3]"

(1..1000).joinToString(limit = 5)
// "1, 2, 3, 4, 5, ..."   (truncates after 5, with "..." marker)

limit shows the first n elements then a truncation marker (default "..."). Useful for logs of huge lists.

With a transform

val people = listOf(
  Person("Alice", 30),
  Person("Bob", 25),
  Person("Charlie", 35),
)

val names = people.joinToString(", ") { it.name }
// "Alice, Bob, Charlie"

val descriptions = people.joinToString(", ") { "${it.name} (${it.age})" }
// "Alice (30), Bob (25), Charlie (35)"

The transform lambda runs on each element, returning a CharSequence. Lets you format complex objects without a separate map { ... }.joinToString() chain.

Versus map().joinToString()

people.map { it.name }.joinToString(", ")
// Equivalent, but allocates an intermediate List<String>

people.joinToString(", ") { it.name }
// One pass, no intermediate

The version with the transform is one allocation cheaper. For large lists, that matters.

On non-list iterables

// Map
val map = mapOf("a" to 1, "b" to 2)
val result = map.entries.joinToString(", ") { "${it.key}=${it.value}" }
// "a=1, b=2"

// Set
val set = setOf("apple", "banana")
println(set.joinToString())   // "apple, banana"

// String → its chars
"hello".toList().joinToString("-")   // "h-e-l-l-o"
"hello".joinToString("-") { it.toString() }   // "h-e-l-l-o" — directly on String

// Array
intArrayOf(1, 2, 3).joinToString(", ")   // "1, 2, 3"

joinToString works on Iterable<T>, Array<T>, and primitive arrays. The signatures vary slightly but the API is the same.

Joining keys, values, or both for a Map

val pairs = mapOf("a" to 1, "b" to 2, "c" to 3)

pairs.keys.joinToString(", ")
// "a, b, c"

pairs.values.joinToString(", ")
// "1, 2, 3"

pairs.map { (k, v) -> "$k=$v" }.joinToString(", ")
// "a=1, b=2, c=3"

Or with the transform:

pairs.entries.joinToString(", ") { (k, v) -> "$k=$v" }

CSV output

val rows = listOf(
  listOf("Alice", "30", "Engineer"),
  listOf("Bob", "25", "Designer"),
)

val csv = rows.joinToString("\n") { row -> row.joinToString(",") }
println(csv)
// Alice,30,Engineer
// Bob,25,Designer

Two joinToString calls — outer joins rows with newlines, inner joins fields with commas.

For real CSV, watch out for fields containing commas, newlines, or quotes — they need quoting/escaping. For toy CSV like this, the bare joinToString is fine.

Common mistakes

Forgetting the separator default. joinToString() without args is ", "-separated, including a space. For tight output (1,2,3), pass "," explicitly.

Using String.join. That's a Java method that exists on Kotlin Strings too, but joinToString is more powerful (transform, limit, prefix/postfix).

joinToString then joinToString chain. For nested data, chaining is fine. For deep trees, write a recursive printer instead.

Concatenating with +. nums.fold("") { acc, n -> "$acc, $n" } works but produces , 1, 2, 3 (leading comma). Use joinToString.

limit = 5 without truncated. Shows "1, 2, 3, 4, 5, ...". Customise: truncated = "[and more]".

What's next

Episode 33: Filter an array. arrayOf(...).filter { ... } returns a List — one of the oddities of mixing arrays and the functional Kotlin stdlib.

Recap

numbers.joinToString(", ") for joining. Optional prefix, postfix, limit, truncated. Optional transform: (T) -> CharSequence to format each element inline (saves a map). Works on Iterable<T>, Array<T>, primitive arrays, and String. For nested joins, chain calls. For real CSV, use a library — joinToString doesn't escape special chars.

Next episode: filter an array.

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.