How to filter a collection using a list of predicates

Viewed 228

I have a list of elements, for example:

val myList = listOf(1,2,3,4,5,6,7)

The list could be of any type, this is just an example. Now I have a list of predicates of arbitrary length:

val myPredicates = listOf({myInt: Int -> myInt > 1}, {myInt: Int -> myInt%2 == 0})

How do I filter the list by applying all the predicates, in order of the list, to myList and get the result in a new list? I feel like the answer lies somewhere in using the reduce or fold operator, but the answer is eluding me.

2 Answers

The simplest solution I can think of is using all on the predicates list in your filter call like this:

myList.filter { elt -> myPredicates.all { it(elt) } }

The part myPredicates.all { it(elt) } returns true when all predicates are true for the given element elt.

One way is to first combine all the predicates into one predicate ((T) -> Boolean) first, using fold, then apply that predicate using the built in filter method.

fun <T> Iterable<T>.filter(predicates: Iterable<(T) -> Boolean>) =
    predicates.fold({ true }) { acc: (T) -> Boolean, next ->
        { acc(it) && next(it) }
    }.let { this.filter(it) }

fun main() {
    val myList = listOf(1,2,3,4,5,6,7)
    val myPredicates = listOf({myInt: Int -> myInt > 2}, {myInt: Int -> myInt%2 == 0})
    println(myList.filter(myPredicates))
    // [4, 6]
}

Because of the short-circuiting nature of && and the order of acc(it) followed by next(it), each predicate will get run in order, and if any one produces false, the rest will not get run.

Related