Provide a way to specify a default value for `fun Iterable<T>.average()`


#1

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.


#2

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

#3

You can also do:

val average = list.average()
    .takeIf { !it.isNaN() } ?: 0

#4

Good one :grinning:


#5

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.