Kotlin is a modern, type-safe programming language that has been designed to be concise, expressive, and easy to use. One of the useful functions provided by Kotlin is maxBy()
and minBy()
. These functions are used to find the maximum and minimum elements of a collection based on the result of a selector function that is applied to each element. However, what happens if there is a tie?
While working on Raderie I needed to take a deep dive into sorting the human values it compares, so I did some reading and experimenting with it. I wanted to share my findings so others could find it more easily.
The maxBy() function
The maxBy()
function is used to find the element in a collection that has the highest value based on the result of a selector function. Here’s an example:
val fruits = listOf("apple", "banana", "orange")
val longestFruit = fruits.maxBy { it.length }
In this example, longestFruit
will be assigned the value "banana"
since it has the longest length among all the fruits.
But what happens if two or more elements have the same highest value? For example, what if we have a list of names and two or more names have the same length?
val names = listOf("Alice", "Bob", "Charlie", "Dave")
val longestName = names.maxBy { it.length }
In this case, the result of maxBy()
is not well-defined since both "Charlie"
and "Dave"
have a length of 6, which is the maximum length among all the names.
Handling ties with maxBy()
When there is a tie, maxBy()
returns the first element that matches the highest value based on the selector function. In other words, it returns the element that appears first in the original collection. This behavior is consistent with other functions in Kotlin that return a single element from a collection, such as first()
and last()
.
So, in the example above, longestName
will be assigned the value "Charlie"
, since it appears first in the original collection.
If you want to get all the elements that match the highest value, you can use the maxByOrNull()
function, which returns a nullable value:
val longestNames = names.filter { it.length == names.maxByOrNull { it.length }?.length }
In this example, longestNames
will be a list containing both "Charlie"
and "Dave"
, since they both have a length of 6, which is the maximum length among all the names.
The minBy() function
The minBy()
function works in a similar way to maxBy()
, but it returns the element with the lowest value based on the result of a selector function. For example:
val prices = mapOf("apple" to 1.99, "banana" to 0.99, "orange" to 1.49)
val cheapestFruit = prices.minBy { it.value }
In this example, cheapestFruit
will be assigned the entry ("banana", 0.99)
since it has the lowest price among all the fruits.
Again, if there is a tie, minBy()
returns the first element that matches the lowest value based on the selector function.
Conclusion
In this blog post, we’ve seen how the maxBy()
and minBy()
functions work in Kotlin and how they handle ties. When there is a tie, these functions return the first element that matches the highest or lowest value based on the selector function. If you want to get all the elements that match the highest or lowest value, you can use the maxByOrNull()