14

How can I destruct a list in Kotlin into two sublists? Currently I do something like this:

val (first, rest) = listOf("one", "two", "three")

But doing so, first is "one" and rest is "two". I want them to be first =["first"] and rest = ["two", "three"].

Is this even possible using this "destructor" syntax?

Willi Mentzel
  • 21,499
  • 16
  • 88
  • 101
Ueli Hofstetter
  • 2,109
  • 2
  • 21
  • 48
  • 1
    Nope, Kotlin doesn't use the head/tail paradigm of FP languages. On your own list type you could define your own `component0` and `component1` methods with the behavior you want, though. But the head/tail idiom is useful in tail-recursive functions, which aren't really the way you should write Kotlin code. – Marko Topolnik Jan 12 '18 at 18:19

4 Answers4

16

Destructuring translates to calling component1, component2, etc. operator functions on an object. In the case of a List, these are defined as extensions in the standard library, and return the Nth element respectively.


You could define your own extension extension that splits the list as desired and returns a Pair, which can then be destructured:

fun <T> List<T>.split() = Pair(take(1), drop(1))

This could be used like so:

val (first, rest) = listOf("one", "two", "three").split()

println(first) // [one]
println(rest)  // [two, three]

Perhaps naming it something better than split would be smart though.

zsmb13
  • 69,803
  • 10
  • 174
  • 178
  • 2
    I had virtually the same thing typed out except mine was just called "destructure". :) Good one. – Todd Jan 12 '18 at 18:22
7

You could also define your own component functions:

operator fun <T> List<T>.component2(): List<T> = this.drop(1)

And then this works as expected:

val (head, rest) = listOf("one", "two", "three")
println(head) // "one"
println(rest) // ["two", "three"]
Todd
  • 26,412
  • 10
  • 67
  • 80
  • The only thing I don't like about this approach is that component3, component4, etc. Will still give you the 3rd and 4th element – jivimberg Jun 09 '18 at 22:40
3

This can be achieved using an extension function. Here's my take:

val <T> List<T>.tail: List<T>
    get() = subList(1, size)

val <T> List<T>.head: T
    get() = first()

fun <T> List<T>.headTail() = Pair(head, tail)

First I'm defining the extensions properties head and tail so you can do:

val list = listOf("one", "two", "three")
println(list.head) // "one"
println(list.tail) // ["two", "three"]

(If you don't like having the extension properties you can simply inline the code in the headTail function)

Finally the headTail function can be used like this:

val (head, tail) = listOf("one", "two", "three").headTail()
println(head) // "one"
println(tail) // ["two", "three"]

Notice that I'm using subList for the tail instead of drop(1) to prevent copying the list every time.

jivimberg
  • 642
  • 1
  • 8
  • 18
2

It is possible by creating component2 operator as extension method manyally:

operator fun <T> List<T>.component2(): List<T> = drop(1)

fun destrcutList() {
    val (first: String, second: List<String>) = listOf("1", "2", "3")
}

You need create extension method only for component2, component1 will be used as previously.

Types can be omitted:

fun destrcutList() {
    val (first, second) = listOf("1", "2", "3")
    println(second[0]) // prints "2"
}

One important note: in case if you declare extension method in another package you have to import function manually:

import your.awesome.package.component2
hluhovskyi
  • 7,201
  • 4
  • 23
  • 40