In this short article we will be looking at how can we group elements in Kotlin or to say it in a different way, how to convert a flat collection to a Map in Kotlin. There are different operations over collections that you will normally find on a day-to-day job as a software developer, this kind of transformation is normally used to categorise data in different groups. Learning how to group elements in Kotlin will be very useful in those jobs where categorising data is needed.
Grouping elements is a way to categorise elements by associating them to a given key of a map. Let’s see how can we achieve that.
groupBy
The groupBy
method allows us to group the elements in a collection using one field in an element or a combination of them.
Looking at a simple unit test you will be able to understand how it works easily. For this test we have created the following User
class.
data class User(val name: String, val age: Int, val gender: Gender)
enum class Gender {
FEMALE,
MALE
}
Using this User
object to create a collection, our test will look like this:
@Test
fun `should group users by gender`() {
val users: List<User> = listOf(
User("John Smith", 39, Gender.MALE),
User("Jennifer Lawrence", 35, Gender.FEMALE),
User("Jack Johnson", 27, Gender.MALE),
User("Robert Downey", 51, Gender.MALE),
User("Jessica Fletcher", 63, Gender.FEMALE),
)
val usersByGender = users.groupBy { user -> user.gender }
assertThat(usersByGender).isEqualTo(mutableMapOf<Gender, List<User>>(
Gender.FEMALE to listOf(
User("Jennifer Lawrence", 35, Gender.FEMALE),
User("Jessica Fletcher", 63, Gender.FEMALE)
),
Gender.MALE to listOf(
User("John Smith", 39, Gender.MALE),
User("Jack Johnson", 27, Gender.MALE),
User("Robert Downey", 51, Gender.MALE)
)
))
}
We have created an initial list containing five users and we use one of its fields, the gender, to group users by gender. The resulting collection is a Kotlin Map whose key is of type Gender
and its value is a list of users.
That looks easy, right? What if we needed to count the numbers of users for each gender? This is where groupingBy
method comes quite handy. It’s a different way to group elements in Kotlin but with a bit more of flexibility, let’s see how it works!

groupingBy
The method groupingBy
returns an object which implements the Grouping
interface. The Grouping
interface provides a series of methods that allow applying functions to each group at one time. We’ll be looking at the different options that this interface provides and how can we take advantage of them in one of our next articles.
For the time being, let’s look at an example using our previous test, with the difference that this time we’ll be counting the number of users for each gender.
@Test fun `should count users by gender`() { val users: List<User> = listOf( User("John Smith", 39, Gender.MALE), User("Jennifer Lawrence", 35, Gender.FEMALE), User("Jack Johnson", 27, Gender.MALE), User("Robert Downey", 51, Gender.MALE), User("Jessica Fletcher", 63, Gender.FEMALE), ) val usersByGender = users.groupingBy { user -> user.gender }.eachCount() assertThat(usersByGender).isEqualTo(mutableMapOf<Gender, Int>( Gender.FEMALE to 2, Gender.MALE to 3 )) }
As you can see, we have modified our test to return the count for each group by combining groupingBy
and eachCount
methods. GroupingBy
is very powerful for aggregate operations, we’ll see more about that in our article “How to use aggregate operations in Kotlin”.
groupByTo
In the same way that we learned how to use filterTo
in our article “How to filter collections in Kotlin“, we can also find groupByTo
method to be able to group elements in Kotlin and append the result to an existing map.
As we just said, this method allows us to append the result to an existing map, this map has to be a mutable map then. Let’s see how can we use it.
@Test
fun `should group users by gender into an existing map`() {
val existing = mutableMapOf(
Gender.FEMALE to mutableListOf(
User("Jennifer Lawrence", 35, Gender.FEMALE),
),
Gender.MALE to mutableListOf(
User("Jack Johnson", 27, Gender.MALE),
User("Robert Downey", 51, Gender.MALE),
)
)
val users: List<User> = listOf(
User("Jennifer Aniston", 47, Gender.FEMALE),
User("Alfred Hitchcock", 90, Gender.MALE),
User("James Rodriguez", 34, Gender.MALE),
User("Jessica Fletcher", 63, Gender.FEMALE),
)
val usersByGender = users.groupByTo(existing) { user -> user.gender }
assertThat(usersByGender).isEqualTo(mutableMapOf<Gender, List<User>>(
Gender.FEMALE to listOf(
User("Jennifer Lawrence", 35, Gender.FEMALE),
User("Jennifer Aniston", 47, Gender.FEMALE),
User("Jessica Fletcher", 63, Gender.FEMALE)
),
Gender.MALE to listOf(
User("Jack Johnson", 27, Gender.MALE),
User("Robert Downey", 51, Gender.MALE),
User("Alfred Hitchcock", 90, Gender.MALE),
User("James Rodriguez", 34, Gender.MALE)
)
))
}
You can see how we have an existing
map with some users grouped by gender and then we group another list of users to concatenate the results to the existing map.
The groupByTo
method has another form that also accepts an additional argument to transform the collection elements. Let’s see how!
groupByTo (with transform)
As we just said, this groupByTo
method allows us to transform an object in the same way that map
does in any Kotlin collection by providing an additional argument. This means that by using this method we can group elements in Kotlin and, on top of that, apply a transformation to each element!
We’re going to reuse our test example once more with one simple tweak. We have now an existing map with users whose name is in uppercase but the second list has its users’ names not capitalised. Therefore we’d like to homogenise the format in both collections by transforming the name to uppercase.
Let’s see how that would look like.
@Test
fun `should group users by gender into an existing map changing name to uppercase`() {
val existing = mutableMapOf(
Gender.FEMALE to mutableListOf(
User("JENNIFER LAWRENCE", 35, Gender.FEMALE),
),
Gender.MALE to mutableListOf(
User("JACK JOHNSON", 27, Gender.MALE),
User("ROBERT DOWNEY", 51, Gender.MALE),
)
)
val users: List<User> = listOf(
User("Jennifer Aniston", 47, Gender.FEMALE),
User("Alfred Hitchcock", 90, Gender.MALE),
User("James Rodriguez", 34, Gender.MALE),
User("Jessica Fletcher", 63, Gender.FEMALE),
)
val usersByGender = users.groupByTo(existing, { user -> user.gender }) {
user -> User(user.name.uppercase(), user.age, user.gender)
}
assertThat(usersByGender).isEqualTo(mutableMapOf<Gender, List<User>>(
Gender.FEMALE to listOf(
User("JENNIFER LAWRENCE", 35, Gender.FEMALE),
User("JENNIFER ANISTON", 47, Gender.FEMALE),
User("JESSICA FLETCHER", 63, Gender.FEMALE)
),
Gender.MALE to listOf(
User("JACK JOHNSON", 27, Gender.MALE),
User("ROBERT DOWNEY", 51, Gender.MALE),
User("ALFRED HITCHCOCK", 90, Gender.MALE),
User("JAMES RODRIGUEZ", 34, Gender.MALE)
)
))
}
In the final results you can see that all the users have their names capitalised. As this method accepts two function arguments, we have to move the first function inside of the enclosing parenthesis. In case you don’t know it already, Kotlin allows specifying the last function argument within curly brackets and we still define the transform function within the curly brackets.
If you are interested in knowing about Kotlin in more detail, we recommend the following books for your own reading:
Conclusion
We’ve seen how to group elements in Kotlin by using groupBy
, groupByTo
and groupingBy
methods. These methods allow us to do different kind of aggregation operations that can be very useful in our day-to-day work.
If you’re interested in reading more Kotlin articles, you can find more of our Kotlin articles here.
That’s all from us today! We hope you’ve found this article useful and hopefully learned something new.
Please follow us if you’re interested in our articles. We hope to see you with us again very soon!
You must log in to post a comment.