Regarding maxBy, I have the following situation: I...
# stdlib
m
Regarding maxBy, I have the following situation: I want to use maxBy for a collection on a value which is nullable, and if the value is null I don’t actually want it but instead return null. The selector of maxBy requires the return value to be not null, so I have to write
maxBy { 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)
Copy code
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.
d
maxBy
should have an overload that takes a
Comparator
, so that you could use
nullsLast
here.
s
The ideomatic name would probably be
maxByNotNull
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
.
j
if @maxmello writes it it can be maxByMax