1. Overview

In this tutorial, we’ll discuss the different ways of filtering a List.

2. Filtering a List

The standard Kotlin library provides many beneficial functions to filter a List. These functions return a new List and are usable for both read-only and mutable Lists.

We use predicates to define the filtering conditions. The predicates are simple lambda expressions. These expressions return true when the provided element matches the predicate and false when it doesn’t match.

2.1. filter() and filterTo()

We can use the filter() function to return a List of elements that match a given predicate:

val countries = listOf("Germany", "India", "Japan", "Brazil", "Australia")
val filterList = countries.filter { it.length > 5 }

assertEquals(3, list.size)
assertTrue(list.containsAll(listOf("Germany","Brazil","Australia")))

In this example, we’re creating a read-only List using the listOf() function. Then we’re filtering the List based on the length of the elements that are greater than 5.

Subsequently, we can use the filterTo() function to append a List of elements matching the given predicate to the destination List:

var list = mutableListOf("United States", "Canada")
countries.filterTo(list, { it.length > 5 })

assertEquals(5, list.size)
assertTrue(list.containsAll(listOf("United States","Canada","Germany","Brazil","Australia")))

Here, we’re creating a MutableList using mutableListOf() function. Then we’re passing this List as the destination List in the first parameter of the filterTo() function call.

2.2. filterNot() and filterNotTo()

To return a List of elements that do not match the given predicate, we can use the filterNot() function:

val filterList = countries.filterNot { it.length > 5 }

assertEquals(2, filterList.size)
assertTrue(list.containsAll(listOf("India","Japan")))

In the example above, the filterNot() function returns the two elements from the List of countries for which the predicate yields false.

Likewise, we can use the filterNotTo() function to append a List of elements not matching the given predicate to a destination List:

var list = mutableListOf("United States", "Canada")
countries.filterNotTo(list, { it.length > 5 })

assertEquals(4, list.size)
assertTrue(list.containsAll(listOf("United States","Canada","India","Japan")))

In this case, the filterNotTo() function appends the two elements that do not match the predicate to the mutableList.

2.3. filterIndexed() and filterIndexedTo()

We can use the filterIndexed() function to utilize the element position in the filter. The predicate of this function uses both the index and the element as arguments. It then returns a List of elements matching the given predicate:

val filterList = countries.filterIndexed { index, it -> index != 3 && it.length > 5 }

assertEquals(2, filterList.size)
assertTrue(list.containsAll(listOf("Germany","Australia")))

In this example, the filterIndexed() function removes the element at index position 3 and those that have lengths less than 5.

Similarly, the filterIndexedTo() function uses both the index and the element as arguments for the predicate. It then appends a List of elements matching the given predicate to the destination List:

var list = mutableListOf("United States", "Canada")
return countries.filterIndexedTo(list, { index, it -> index != 3 && it.length > 5 })

assertEquals(4, list.size)
assertTrue(list.containsAll(listOf("United States","Canada","Germany","Australia")))

In this example, the filterIndexedTo() function appends the resulting two elements matching the predicate to the mutableList.

2.4. filterIsInstance() and filterIsInstanceTo()

The filterIsInstance() function can be used to return a List of elements of a specified type:

val countryCode = listOf("Germany", 49, null, "India", 91, "Japan", 81, "Brazil", null, "Australia", 61)
val filterList = countryCode.filterIsInstance<Int>()

assertEquals(4, filterList.size)
assertTrue(list.containsAll(listOf(49,91,81,61)))

In this case, we’re using the filterIsInstance() function to return a List of elements of type Integer.

Subsequently, we can use the filterIsInstanceTo() function to append a List of elements of a specified type to the destination List:

val countryCode = listOf("Germany", 49, null, "India", 91, "Japan", 81, "Brazil", null, "Australia", 61)
var list = mutableListOf(1, 24)
countryCode.filterIsInstanceTo(list)

assertEquals(6, list.size)
assertTrue(list.containsAll(listOf(1,24,49,91,81,61)))

In this example, we’re appending the List of elements of type Integer returned by the filterIsInstanceTo() function to the mutableList.

2.5. filterNotNull() and filterNotNullTo()

To return a non-null List of elements we can use the filterNotNull() function:

val countries = listOf("Germany", "India", null, "Japan", "Brazil", null, "Australia")
val filterList = countries.filterNotNull()

assertEquals(5, filterList.size)
assertTrue(list.containsAll(listOf("Germany","India","Japan","Brazil","Australia")))

The resulting List contains only non-null elements returned by the filterNotNull() function in the case above.

*Similarly, we can use the filterNotNullTo() function to append a non-null List of elements to the destination List*:

val countries = listOf("Germany", "India", null, "Japan", "Brazil", null, "Australia")
var list = mutableListOf("United States", "Canada") 
countries.filterNotNullTo(list)

assertEquals(7, list.size)
assertTrue(list.containsAll(listOf("United States","Canada","Germany","India","Japan","Brazil","Australia")))

In this example, the non-null elements from the List of countries are appended to the mutableList.

3. Filter a List In-Place

To filter a List in-place means to modify the original list without re-creating a new List. For this purpose, the items should be of type MutableList.

3.1. remove()

One way to filter a mutable List in-place by iterating through the List using an iterator. Then we can remove the elements using Iterator’s remove() function:

val countries = mutableListOf("Germany", "India", "Japan", "Brazil", "Australia")
val iterator = countries.iterator()
while (iterator.hasNext())
{
    val current = iterator.next()
    if (current.length > 5) {
        iterator.remove()
    }
}

assertEquals(2, countries.size)
assertTrue(list.containsAll(listOf("India","Japan")))

In the example above, we’re using an iterator to iterate the mutable List of countries and remove the elements.

3.2. removeAll()

Alternatively, we can use the removeAll() function to remove the elements of the mutable List matching the given predicate:

val countries = mutableListOf("Germany", "India", "Japan", "Brazil", "Australia")
countries.removeAll { it.length > 5 }

assertEquals(2, countries.size)
assertTrue(list.containsAll(listOf("India","Japan")))

In this case, we’re using the removeAll() function to remove all the elements whose length is more than 5.

3.3. retainAll()

Finally, we can use the retainAll() function to retain the elements of the mutable List matching the given predicate:

val countries = mutableListOf("Germany", "India", "Japan", "Brazil", "Australia")
countries.retainAll { it.length > 5 }

assertEquals(3, countries.size)
assertTrue(list.containsAll(listOf("Germany","Brazil","Australia")))

Here, we’re using the retainAll() function to retain all the elements whose length is more than 5.

4. Conclusion

In this article, we discussed the various functions for filtering a List.

As always, the code for these examples is available over on GitHub.