1. Introduction
Lists are one of the basic collections in Kotlin, and converting a collection of elements to a plain string is a pretty common use case. In this tutorial, we’ll learn how to easily convert a List to a String.
2. Converting List to a String Using joinToString
Kotlin Collections API has many useful methods for performing operations over collections. One of them is joinToString. This method creates a string from all the elements in a collection. The default separator is a comma.
Let’s define our list and check the output of joinToString:
val numbers = listOf(11, 22, 3, 41, 52, 6)
val string = numbers.joinToString()
assertEquals("11, 22, 3, 41, 52, 6", string)
We can define separator, prefix, and suffix parameters to customize the output string:
val numbers = listOf(11, 22, 3, 41, 52, 6)
val string = numbers.joinToString(prefix = "<", postfix = ">", separator = "")
assertEquals("<1122341526>", string)
We can also set a limit if our list is too long, and we can even set a transform lambda:
val chars = charArrayOf('h', 'e', 'l', 'l', 'o', 'o', 'o', 'o')
val string = chars.joinToString(separator = "", limit = 5, truncated = "!") { it.uppercaseChar().toString() }
assertEquals("HELLO!", string)
3. Appending a List to an Existing Text Using joinTo
If we already have a string or a piece of text and want to add elements of our list to it, we can use the joinTo method. It appends the string formed from all the list elements to an existing string using StringBuilder. As in the previous example, if we provide parameters, we can customize our output string:
val sb = StringBuilder("An existing string and a list: ")
val numbers = listOf(11, 22, 3, 41, 52, 6)
val string = numbers.joinTo(sb).toString()
assertEquals("An existing string and a list: 11, 22, 3, 41, 52, 6", string)
As a matter of fact, joinTo takes any class implementing the Appendable interface as its buffer argument. However, only StringBuilder does that in the standard library. The method returns the same type as its buffer argument, so it most likely will be a StringBuilder.
4. Creating a String From a List With a Feedback Using reduce and fold
We can also use the reduce function to form a string out of a list. The reduce function accumulates value starting with the first element. Then, it applies the operation from left to right to the current accumulator value and each subsequent element. Therefore, each next value of the accumulator may depend on what value it already holds:
val strings = listOf("a", "b", "a", "c", "c", "d", "b", "a")
val uniqueSubstrings = strings.reduce { acc, string -> if (string !in acc) acc + string else acc }
assertEquals("abcd", uniqueSubstrings)
As written, this code will instantiate a new String instance on each iteration. It means that, over large collections, such an approach may be quite detrimental to the application performance.
Secondly, an empty collection can’t be reduced; it throws an exception, instead. So, we have to closely watch the part of our code where we use reduce.
Another approach to the same task might be the fold function. It is to reduce as joinTo is to joinToString – we have to provide an initial value, which is used as an accumulator on the first iteration:
val strings = listOf("a", "b", "a", "c", "c", "d", "b", "a")
val uniqueSubstrings =
strings.fold(StringBuilder()) { acc, string -> if (string !in acc) acc.append(string) else acc }
assertEquals("abcd", uniqueSubstrings.toString())
This way, we can use StringBuilder over our collection, thus sidestepping the memory and performance issues of reduce.
5. Looping
Finally, we can use simple concatenation to build the string character by character. We can do this by looping over the list and concatenating each character to the string one at a time. Let’s see an example:
val elements = listOf("a", "b", "c", "d", "e")
var string = ""
for(s in elements){
string += s
}
assertEquals("abcde", string)
This approach, similar to what we’ve just seen with reduce, is a danger when large collections are involved. Instead, we can use StringBuilder to do the same:
val letters = listOf("a", "b", "c", "d", "e", "f")
val builder = StringBuilder()
for(s in letters){
builder.append(s)
}
assertEquals("abcdef", builder.toString())
In fact, Kotlin has a nice piece of syntax sugar to make handling a StringBuilder much more comfortable:
val letters = listOf("a", "b", "c", "d", "e", "f")
val alreadyAString = buildString { for (s in letters) append(s) } // `this` is a StringBuilder inside the lambda
assertEquals("abcdef", alreadyAString)
To customize the output, we can use a built-in function like removeSuffix() after returning the StringBuilder as a String:
val letters = listOf("a", "b", "c", "d", "e", "f")
val string = buildString { letters.forEach(::append) }
val withoutSuffix = string.removeSuffix("f")
assertEquals("abcde", withoutSuffix)
6. Conclusion
In this article, we’ve seen the most common ways to convert a list to a string in Kotlin. Using Kotlin’s built-in functions allows our code to be cleaner and follow the conventions of the language. We can also easily customize our output string. As usual, all the examples are available over on GitHub.