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