maxmello
05/21/2020, 2:03 PMmaxBy { it.someNullableValue ?: Long.MIN_VALUE }
.
This will return me the first value if the list is not empty, so I would have to append .takeIf{ it?.someNullableValue != null}
, which is fine for short cases like this, but when the value to compare is not top level but itself created by a more complex expression like it.someList.filter{…}.maxBy{…}?.someNullableValue
you need to write that same complex expression twice in the takeIf
part. So I wrote maxByOrNullIfNull
(kind of a bad name I know)
inline fun <T, R : Comparable<R>> Iterable<T>.maxByOrNullIfNull(selector: (T) -> R?): T? { // Allows selector to return R?
val iterator = iterator()
if (!iterator.hasNext()) return null
var maxElem: T? = iterator.next()
var maxValue: R? = selector(maxElem!!) // Here, we know maxElem is not null
if(maxValue == null) {
maxElem = null
}
if (!iterator.hasNext()) return maxElem
do {
val e: T = iterator.next()
val v: R? = selector(e)
if(v != null) {
if(maxValue == null) {
// If we don't have any non-null element yet, set the current element / value
maxElem = e
maxValue = v
} else if (maxValue < v) {
// Here, we know both values are not null, so we do the usual comparison
maxElem = e
maxValue = v
}
}
} while (iterator.hasNext())
return maxElem
}
I would argue that this would be a nicer default behavior for maxBy
, as it doesn’t really change the behavior for non-null cases, but makes clear that null values will always be considered “less” than any actual value in the comparison (no more need for ?: Long.MIN_VALUE
or something) and that we want an element with an actual value, not one that has no value for the comparison.Derek Peirce
05/21/2020, 9:32 PMmaxBy
should have an overload that takes a Comparator
, so that you could use nullsLast
here.spand
05/22/2020, 3:15 PMmaxByNotNull
in the same vein as mapNotNull
.
I wont argue either way other than its a bit ambiguous if a return value of null
indicates no not null values or that the max value was in fact null
.jimn
05/22/2020, 4:37 PM