1. Overview

A Map maintains key-value associations. Working with Kotlin maps is a common occurrence. Sometimes, we want to find the maximum entry from a Kotlin Map. This task is a common operation and presents an exciting challenge.

In this tutorial, we’ll discuss the different scenarios of this problem and explore how to find the maximum entry in a Kotlin Map.

2. Introduction to the Problem

Before we dive into the Kotlin code, let’s clarify the problem. A map entry contains a key and a value. Therefore, a specified requirement defines what the maximum entry means.

Let’s understand this quickly through an example:

val skillMap = mapOf(
  "Kai" to "Kotlin, typescript, Javascript, C, C++",
  "Saajan" to "Java, C, Python, Ruby, Shell, Go",
  "Eric" to "Kotlin, Java, C, Python, Ruby, Php, Shell, Rust",
  "Kevin" to "Java, Scala, Go, Ruby",
  "Venus" to "Beauty!"
)

As the example above shows, the skillMap is a Map<String, String>. It carries persons as keys and their skills in a CSV format as values.

Depending on requirements, the definition of the maximum entry can vary, for instance:

  • Value-based – The entry with the most skills
  • Key-based – The entry with the longest name

Of course, there can be more possible “maximum entry” definitions. In this tutorial, we’ll take the above two scenarios as examples to address methods to find the maximum entry. If we master the techniques, we can adapt the code to fit a new requirement. Also, for simplicity, we’ll use unit test assertions to verify whether each approach produces the expected value.

Let’s dive in.

3. Finding the Entry With the Most Skills

Let’s first take the value-based maximum entry as an example and find the entry with the most skills. The most straightforward approach would be looping through all entries, counting the skills in each entry, and finally finding the entry with the largest count.

Kotlin offers a lot of convenient functions that allow us to craft concise and highly readable code. For example, *we can use Map‘s maxBy() function to achieve the goal* instead of writing the loop and comparing logic by ourselves:

val result = skillMap.maxBy { (_, skills) -> skills.split(", ").size }
assertEquals("Eric" to "Kotlin, Java, C, Python, Ruby, Php, Shell, Rust", result.toPair())

As the code above shows, in the maxBy() function, we must provide a function to tell maxBy() what value we want to compare among all map entries to find the maximum entry. In this example, we pass a lambda expression to split the skills string by comma and count the split result.

It’s worth noting that since we only need each entry’s “value” for the lambda expression, we put an underscore placeholder at the “key” position.

Alternatively, we can use the maxWith() function to do the job:

val result = skillMap.maxWith { e1, e2 ->
    e1.value.split(", ").size.compareTo(e2.value.split(", ").size)
}
assertEquals("Eric" to "Kotlin, Java, C, Python, Ruby, Php, Shell, Rust", result.toPair())

The maxWith() function takes a Comparator object as its argument and finds the maximum element based on the comparison the Comparator defines.** Again, we passed a lambda expression as the Comparator implementation.

4. Finding the Entry With the Longest Name

We’ve solved the first scenario of the problem: finding the value-based maximum entry from a map using the handy functions maxBy() and maxWith().

Since maxBy() or maxWith() accept a function to find the maximum entry, we can easily adjust them to adapt to new requirements. So next, let’s first look at how to use the maxBy() function to find the key-based maximum entry:

val result1 = skillMap.maxBy { (name, _) -> name.length }
assertEquals("Saajan" to "Java, C, Python, Ruby, Shell, Go", result1.toPair())

This time, as the code above shows, we put an underscore placeholder at the “value” position and only take the “key” (name) for comparison. To find the entry with the longest name, we need to compare each name’s length: name.length. As a result, the entry with the longest name (“Saajan”) is returned.

Similarly, the maxWith() can do the job if we pass it a comparator to compare entries.key‘s length:

val result2 = skillMap.maxWith { e1, e2 -> e1.key.length.compareTo(e2.key.length) }
assertEquals("Saajan" to "Java, C, Python, Ruby, Shell, Go", result2.toPair())

5. A Few Words About the maxOf() Function

Apart from maxBy() and maxWith(), Map offers another “max”-related function: maxOf().

Both maxBy() and maxWith() return the maximum map entry calculated by the function we passed to them. maxOf() also expects a selector function. However, it returns the largest value among all values produced by the selector function applied to each entry in the map. In other words, the maxOf() function returns the maximum value that the selector function returned. So, it doesn’t always produce a map entry. Some examples can clarify it quickly:

val result = skillMap.maxOf { (name, _) -> name }
assertEquals("Venus", result)

In this example, our selector function returns the key of each map entry. So, maxOf() returns the lexicographical max entry key.  Next, let’s change the selector function:

val result = skillMap.maxOf { (name, _) -> name[1] }
assertEquals('r', result)

The selector function returns the second character from each entry key this time. Therefore, maxOf() returns a Char value.

Finally, let’s make the selector function return skill counts of each entry:

val result = skillMap.maxOf { it.value.split(", ").size }
assertEquals(8, result)

As we can see, maxOf() returns the largest skill count in the map instead of the corresponding map entry.

6. Conclusion

In this article, we’ve learned how to find the maximum entry from a Kotlin map using maxOf() and maxWith(). Also, we discussed the usage of the maxOf() function.

As always, the complete source code for the examples is available over on GitHub.