article banner

Exercise: Adding element at position

We can add an element at a specific position to a mutable list using the add method. For instance:

fun main() { val list = mutableListOf(1, 2, 3) list.add(1, 4) println(list) // [1, 4, 2, 3] }

Unfortunately, there is no similar function that would allow us to add an element at a specific position to an immutable list. Your task is to define it, and name it plusAt.

fun <T> List<T>.plusAt(index: Int, element: T): List<T> { TODO() }

It should be used in the following way:

val list = listOf(1, 2, 3) println(list.plusAt(1, 4)) // [1, 4, 2, 3] println(list.plusAt(0, 5)) // [5, 1, 2, 3] println(list.plusAt(3, 6)) // [1, 2, 3, 6] val list2 = listOf("A", "B", "C") println(list2.plusAt(1, "D")) // [A, D, B, C]

This function should check if the index is correct. If it is not, it should throw an IllegalArgumentException. Should consider 0 as a correct index, and add this element to the beginning of the list. Should also consider size as a correct index, and add this element to the end of the list.

I know at least three significantly different ways to implement this function. One uses a mutable collection, while others use collection processing functions and + operator. Try to implement it in all three ways.

This problem can either be solved in the below playground or you can clone kotlin-exercises project and solve it locally. In the project, you can find code template for this exercise in functional/collections/PlusAt.kt. You can find there starting code, example usage and unit tests.

Once you are done with the exercise, you can check your solution here.

Playground

import org.junit.Test import kotlin.test.assertEquals fun <T> List<T>.plusAt(index: Int, element: T): List<T> { TODO() } fun main() { val list = listOf(1, 2, 3) println(list.plusAt(1, 4)) // [1, 4, 2, 3] println(list.plusAt(0, 5)) // [5, 1, 2, 3] println(list.plusAt(3, 6)) // [1, 2, 3, 6] val list2 = listOf("A", "B", "C") println(list2.plusAt(1, "D")) // [A, D, B, C] } class PlusAtTest { @Test fun `Simple addition to the middle adds correctly at the position`() { assertEquals(listOf(1, 2, 7, 3), listOf(1, 2, 3).plusAt(2, 7)) assertEquals(listOf("A", "B", "D", "C"), listOf("A", "B", "C").plusAt(2, "D")) } @Test fun `When we add at size position, element is added at the end`() { assertEquals(listOf(1, 2, 3, 7), listOf(1, 2, 3).plusAt(3, 7)) assertEquals(listOf("A", "B", "C", "D"), listOf("A", "B", "C").plusAt(3, "D")) } @Test fun `When we add at 0, element is added at the beginning`() { assertEquals(listOf(7, 1, 2, 3), listOf(1, 2, 3).plusAt(0, 7)) assertEquals(listOf("D", "A", "B", "C"), listOf("A", "B", "C").plusAt(0, "D")) } @Test fun `When we try to insert at illegal position, IllegalArgumentException error is thrown`() { assertThrows<IllegalArgumentException> { listOf(1, 2, 3).plusAt(-1, 7) } assertThrows<IllegalArgumentException> { listOf(1, 2, 3).plusAt(8, 7) } assertThrows<IllegalArgumentException> { listOf(1, 2, 3).plusAt(10, 7) } assertThrows<IllegalArgumentException> { listOf(1, 2, 3).plusAt(100, 7) } } } inline fun <reified T: Throwable> assertThrows(operation: () -> Unit) { val result = runCatching { operation() } assert(result.isFailure) { "Operation has not failed with exception" } val exception = result.exceptionOrNull() assert(exception is T) { "Incorrect exception type, it should be ${T::class}, but it is $exception" } }