Sazzad Hissain Khan
Sazzad Hissain Khan

Reputation: 40255

Kotlin - How to skip entries on exceptions when mapping and filtering

I wonder how to skip one entry while mapping or filtering?

fun getFilteredList(list: List<String>, match: String): List<String>? {

    val flist = list.filter {
         try {
             // Some parsing logic which can throw exceptions
             val res = SomeParser.parse(match)
             it == res
         } catch (e: Exception){
             // I want to skip this entry to include in flist but still continue 
             // for left of the list entries without return. How to do that?
             return null
         }
     }
     return flist
}

Upvotes: 2

Views: 4655

Answers (1)

Slaw
Slaw

Reputation: 46255

The problem is your use of the return keyword. Your code should look like:

// return type no longer needs to be nullable
fun getFilteredList(list: List<String>, match: String): List<String> {
    val flist = list.filter {
         try {
             val res = SomeParser.parse(match)
             it == res
         } catch (e: Exception){
             true
         }
     }
     return flist
}

Or, based on your example, just (using "single-expression function" syntax):

fun getFilteredList(list: List<String>, match: String) = list.filter {
    try {
        it == SomeParser.parse(match)
    } catch (ex: Exception) {
        true
    }
}

Note: Both of these will call SomeParser.parse for each element in the list. However, you mention this is just an example and the real code works differently (i.e. can't be pulled out of the filter operation).


The reason return was giving you an error has to do with lambda expressions. From the Returning a value from a lambda expression section of the Kotlin reference:

We can explicitly return a value from the lambda using the qualified return syntax. Otherwise, the value of the last expression is implicitly returned.

Therefore, the two following snippets are equivalent:

ints.filter {
    val shouldFilter = it > 0 
    shouldFilter
}

ints.filter {
    val shouldFilter = it > 0 
    return@filter shouldFilter
}

[...]

And this is in combination with the fact filter is an inline function. From the Non-local retuns section of the Kotlin reference:

In Kotlin, we can only use a normal, unqualified return to exit a named function or an anonymous function. This means that to exit a lambda, we have to use a label, and a bare return is forbidden inside a lambda, because a lambda cannot make the enclosing function return:

fun foo() {
    ordinaryFunction {
        return // ERROR: cannot make `foo` return here
    }
}

But if the function the lambda is passed to is inlined, the return can be inlined as well, so it is allowed:

fun foo() {
    inlined {
        return // OK: the lambda is inlined
    }
}

Such returns (located in a lambda, but exiting the enclosing function) are called non-local returns. [...]

This means when you had return null it was actually trying to exit the entire getFilteredList function. I'm assuming this is why you have your return type as List<String>? instead of List<String>. And when you tried return false you were trying to return a Boolean from a function whose return type was List<String>?.

Upvotes: 2

Related Questions