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.