std Java:
double d = collection.stream()
.mapToInt(v -> v.getNumber())
.average()
.orElse(0.0);
This is a nice way to avoid checking for corner cases (empty collection). There is no way to do this in Kotlin. average() returns NaN in case of a empty list.
May I suggest a extension function to Double/Float:
fun Double.orElse(fallback:Double): Double = if (isNaN()) fallback else this
Alternatively add a averageOrNull() method, which will return null instead of NaN.
I’m not sure whether I agree with you on this one because the average function returns NaN not only when the collection is empty but also when one (or more) of the elements in a non-empty collection is itself NaN.
An explicit check for emptiness does at least resolve this situation
If, notwithstanding this problem, a new extension function were introduced, I’d prefer a name such as orIfNaN to be absolutely clear on why the fallback value was being used.
Incidentally, although it’s a bit verbose, you can do something like this just now:
val da = doubleArrayOf()
val av = da.map { it.toInt() }.average().run { if (isNaN()) 0.0 else this }
println(av) // 0.0
You can also do:
val average = list.average()
.takeIf { !it.isNaN() } ?: 0
1 Like
Although this is a bit off topic, @Eliote’s reply raises an interesting (and slightly unnerving) point about how the ‘elvis’ operator works.
I thought at first that - as written - the expression wouldn’t compile because takeIf is returning a Double? and 0 is an integer literal.
However, it does compile and returns Any which means, of course, that you can’t perform any subsequent arithmetic operations on it without casting it to a numeric type first.
This problem disappears if you replace 0 with 0.0. The elvis operator now returns Double as expected.
1 Like