I'm looking for a function to get from the list:
[6, 54, 3, 6, 3]
the following list:
[0, 3]
when I filter on 6.
So far, I've only found functions that let me filter on the first or last occurrence.
There's currently no single function in the standard library for this, but it's easy to write your own, e.g.:
fun <E> Iterable<E>.indexesOf(e: E)
= mapIndexedNotNull{ index, elem -> index.takeIf{ elem == e } }
This will work on any list, collection, or other iterable, of any type, and returns a List<Int>
. If there are no matching items (e.g. if the collection is empty), it returns an empty list.
Here mapIndexedNotNull()
transforms the elements, returning only the non-null ones. And takeIf()
is used to get the index only of matching elements, returning null for the rest (which will then be discarded).
This should be relatively efficient; it does a single scan through the list, and creates only a single new list with the results. (If temporary objects were a major concern, you'd probably not store the matching indices in a list at all, but simply loop through them with some forEachMatchingIndex()
function instead.)
(Thanks to Michael for the initial idea.)
Update: for completeness, here's a version which finds elements satisfying any supplied function:
inline fun <E> Iterable<E>.indexesOf(predicate: (E) -> Boolean)
= mapIndexedNotNull{ index, elem -> index.takeIf{ predicate(elem) } }
You could use this to e.g. find the indexes of all even numbers:
listOf(1, 2, 3, 4, 5, 6).indexesOf{ it % 2 == 0 } // [1, 3, 5]