listkotlinfilter

Get index of all occurrences in a list


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.


Solution

  • 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]