Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to embed for loops in Kotlin string templates

We can easily nest expression operators like if and when in Kotlin string templates:

"List ${if (list.isEmpty()) "is empty" else "has ${list.size} items"}."

But for or while are not expressions and can't be nested in template like this:

"<ol>${for (item in list) "<li>$item"}</ol>"

So I was looking for convinient ways to use loops inside large templates.

like image 225
Vadzim Avatar asked Jan 03 '23 21:01

Vadzim


1 Answers

The easiest out-of-the-box way I found so far is to replace loops with equivalent joinToString calls:

"<ol>${list.joinToString("") { "<li>$it" }}</ol>"

or

"""
<ol>${list.indices.joinToString("") {
    """
    <li id="item${it + 1}">${list[it]}"""
}}
</ol>""".trimIndent()

In the matter of preference, it's also possible to simulate loops with helper functions:

inline fun <T> forEach(iterable: Iterable<T>, crossinline out: (v: T) -> String) 
    = iterable.joinToString("") { out(it) }

fun <T> forEachIndexed1(iterable: Iterable<T>, out: (i: Int, v: T) -> String): String {
    val sb = StringBuilder()
    iterable.forEachIndexed { i, it ->
        sb.append(out(i + 1, it))
    }
    return sb.toString()
}

and use them like this:

"<ol>${forEach(list) { "<li>$it" }}</ol>"

or

"""
<ol>${forEachIndexed1(list) { i, item ->
"""
    <li id="item$i">$item"""
}}
</ol>""".trimIndent()
like image 58
Vadzim Avatar answered Jan 07 '23 19:01

Vadzim