🚗 · Filtering

4 min read · Updated on by

Read on for an introduction to the most important filtering operations: filter, take, drop, slice, distinct, and their variants.

Filtering

filter


//sampleStart
inline fun <T> Iterable<T>.filter(
    predicate: (T) -> Boolean
): List<T>
//sampleEnd

Probably the most ubiquitous of all filtering functions, filter accepts a predicate and returns a List of all the elements for which the predicate returns true.

Example


//sampleStart
fun main() {
    listOf(1, 2, 3, 4, 5).filter {
        it % 2 == 0
    }.also(::println) // listOf(2, 4)
}
//sampleEnd

There are a few useful variants of filter:

One is filterNot, which returns a List of elements for which the predicate returns false.


//sampleStart
inline fun <T> Iterable<T>.filterNot(
    predicate: (T) -> Boolean
): List<T>
//sampleEnd

Example


//sampleStart
fun main() {
    listOf(1, 2, 3, 4, 5).filterNot {
        it % 2 == 0
    }.also(::println) // listOf(1, 3, 5)
}
//sampleEnd

Another is filterIsInstance, which takes a single generic parameter, and returns all elements that are of that type. It should be noted that this also narrows the type of the returned list.


//sampleStart
inline fun <reified R> Iterable<*>.filterIsInstance(): List<R>
//sampleEnd

Example


//sampleStart
fun main() {
    val numbers: List<Number> = listOf(1, 3.5, 5L, 19, 4.6, Math.PI)
    val doubles = numbers.filterIsInstance<Double>().also(::println) // List<Double> { 3.5, 4.6, Math.PI }
    val longs = numbers.filterIsInstance<Long>().also(::println) // List<Long { 5L }
}
//sampleEnd

A kind of combination of the previous to is filterNotNull(), which returns all non-null elements. Notice how the return type is constrained to a non-nullable one.


//sampleStart
fun <T : Any> Iterable<T?>.filterNotNull(): List<T>
//sampleEnd

Example


//sampleStart
fun main() {
    val numbersOrNull: List<Number?> = listOf(1, 3.5, null, 19, 4.6, Math.PI).also(::println)
    val numbers = numbersOrNull.filterNotNull().also(::println) // List<Number> { 1, 3.5, 19, 4.6, Math.PI }
}
//sampleEnd

As with many other collection operations, the *To and *Indexed variants of most of the previous functions are available if you need them.

Subranges

There are many functions that allow you to take a continous subsection of a list according to some criteria.

take, drop


//sampleStart
fun <T> Iterable<T>.take(n: kotlin.Int): List<T>
fun <T> Iterable<T>.drop(n: kotlin.Int): List<T>
//sampleEnd

The take/drop functions return the first n elements of the list/return the list without the first n elements, respectively.

Example


//sampleStart
fun main() {
    val list = listOf(1, 2, 3, 4, 5)
    list.take(3).also(::println) // listOf(1, 2, 3)
    list.drop(4).also(::println) // listOf(5)
}
//sampleEnd

There are a few useful variants:

One are the *Last variants, which do the same thing, but for the last n elements.


//sampleStart
fun <T> List<T>.takeLast(n: Int): List<T>
fun <T> List<T>.dropLast(n: Int): List<T>
//sampleEnd

Example


//sampleStart
fun main() {
    val list = listOf(1, 2, 3, 4, 5)
    list.takeLast(3).also(::println) // listOf(3, 4, 5)
    list.dropLast(4).also(::println) // listOf(1)
}
//sampleEnd

Another are the *While variants, which do the same thing, but instead of taking/dropping a fixed amount of elements, they accept a predicate and take/drop elements while that predicate is true:


//sampleStart
inline fun <T> Iterable<T>.takeWhile(
    predicate: (T) -> Boolean
): List<T>
inline fun <T> Iterable<T>.dropWhile(
    predicate: (T) -> Boolean
): List<T>
//sampleEnd

Example


//sampleStart
fun main() {
    val list = listOf(1, 2, 3, 4, 5)
    list.takeWhile { it < 3 }.also(::println) // listOf(1, 2)
    list.dropWhile { it < 3 }.also(::println) // listOf(3, 4, 5)
}
//sampleEnd

You also have *LastWhile variants, which are the combination of the previous two.


//sampleStart
inline fun <T> Iterable<T>.takeLastWhile(
    predicate: (T) -> Boolean
): List<T>
inline fun <T> Iterable<T>.dropLastWhile(
    predicate: (T) -> Boolean
): List<T>
//sampleEnd

//sampleStart
fun main() {
    val list = listOf(1, 2, 3, 4, 5)
    list.takeLastWhile { it > 3 }.also(::println) // listOf(4, 5)
    list.dropLastWhile { it > 3 }.also(::println) // listOf(1, 2, 3)
}
//sampleEnd

slice


//sampleStart
fun <T> List<T>.slice(indices: IntRange): List<T>
fun <T> List<T>.slice(indices: Iterable<Int>): List<T>
//sampleEnd

The slice function comes in two variants — one returns all the elements in a certain index range, while the other returns the elements at the specified indices. Both could be implemented in terms of filterIndexed.

Example


//sampleStart
fun <T> List<T>.slice(indices: IntRange): List<T> = filterIndexed { idx, _ -> 
    idx in indices
}

fun <T> List<T>.slice(indices: Iterable<Int>): List<T> = filterIndexed { idx, _ -> 
    idx in indices
}

fun main() {
    val list = listOf('a', 'b', 'c', 'd', 'e')
    list.slice(2..4).also(::println) // listOf('c', 'd', 'e')
    list.slice(listOf(1, 3, 4)).also(::println) // listOf('b', 'd', 'e')
}
//sampleEnd

Miscellaneous

distinct, distinctBy


//sampleStart
fun <T> Iterable<T>.distinct(): List<T>
inline fun <T, K> Iterable<T>.distinctBy(
    selector: (T) -> K
): List<T>
//sampleEnd

Returns a List with all duplicate elements removed. The distinctBy variant removes all elements for which selector returns the same value.

Example


//sampleStart
fun main() {
    val list = listOf(1, 3, 1, 2, 1, 2, 3)
    list.distinct().also(::println) // listOf(1, 3, 2)
    list.distinctBy { it % 2 }.also(::println) // listOf(1, 2)
}
//sampleEnd

More information can be found in the docs.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

The Kotlin Primer