Reputation: 2127
So I have parsers and want to use the first that does return a non-null value. How would I do that most elegantly?
return parsers.map { it.parse(content) }.firstOrNull { it != null }
would map all (million?) parsers before picking the first.
return parsers.firstOrNull { it.parse(content) != null }?.parse(content)
would run the (expensive?) parse()
once again.
I know I can
for (parser in parsers) {
val result = parser.parse(content)
if (result != null) {
return result
}
}
return null
parsers.forEach { it.parse(content)?.run { return this } }
return null
is the shortest I can get but it's not nice to read.
I'm pretty sure there is a shortcut here that I don't see.
Upvotes: 0
Views: 666
Reputation: 610
Also, you can use the following code:
parsers.asSequence()
.mapNotNull { it.parse(content) }
.first()
Upvotes: 3
Reputation: 18557
As an alternative to the overhead of a Sequence
, or mapping lots of values unnecessarily, you could use an extension method such as:
inline fun <T, R> List<T>.firstMappedNotNull(transform: (T) -> R): R? {
for (e in this)
return transform(e) ?: continue
return null
}
This uses the minimum of mapping function calls and temporary objects. It's necessarily written in an imperative way, but it's quite short, and makes your own code short, clear, and functional.
(This version returns null
if the list was empty or every mapping returned null
. You could of course change the signature and last line to throw an exception instead.)
It's a shame this function isn't already in the standard library. But it's easy to add your own!
Upvotes: 4
Reputation: 27971
Use a sequence. It makes your computation lazy, so that you will only compute parse
as many times as you need.
return parsers.asSequence()
.map { it.parse(content) }
.find { it != null }
Upvotes: 6