Kotlin Collection Functions

Banner Kotlin Collection Functions

If you are reading this blog, then we are pretty sure that either you have totally shifted to Kotlin for Android Development or you are planning to move to Kotlin very soon. In any of the cases, this blog is a perfect match for you. So, let's get to the point.

In our Android application, we deal with various collections such as lists, maps, sets, etc. We perform many operations on these, but do you know there are many Kotlin Collection Functions, that can make our life easier. There is a high probability that if you are want to perform some operation(simple or complex) on any collection, then there must be some collection function that will make your whole Android App development simpler and easier.

In this blog, we will learn about many of these Kotlin Collection Functions. You can bookmark this blog and keep this blog as the cheat sheet for your Kotlin Collection Functions. So, let's start with a simple operation i.e. removing the duplicate strings from an array.

Remove Duplicate Strings

There are many ways to remove duplicate strings from an array:

// Maintain the original order of items
val devs = arrayOf("Amit", "Ali", "Amit", "Sumit", "Sumit", "Himanshu")
print(devs.distinct()) // [Amit, Ali, Sumit, Himanshu]

// Maintain the original order of items
val devs = arrayOf("Amit", "Ali", "Amit", "Sumit", "Sumit", "Himanshu")
print(devs.toSet()) // [Amit, Ali, Sumit, Himanshu]

// Maintain the original order of items
val devs = arrayOf("Amit", "Ali", "Amit", "Sumit", "Sumit", "Himanshu")
print(devs.toMutableSet()) // [Amit, Ali, Sumit, Himanshu]

// DO NOT Maintain the original order of items
val devs = arrayOf("Amit", "Ali", "Amit", "Sumit", "Sumit", "Himanshu")
print(devs.toHashSet()) // [Amit, Ali, Sumit, Himanshu]

Convert an array or list to a string

You can convert an array or list into a string by using joinToString. For example, if you are having a list of cities(Delhi, Mumbai, Bangalore), then you can convert that list into a string such as "India is one the best country for tourism. You can visit Delhi, Mumbai, Bangalore, etc, and enjoy your holidays". Here, Delhi, Mumbai, Bangalore are the list items which you were having.

val someKotlinCollectionFunctions = listOf(
    "distinct", "map",
    "isEmpty", "contains",
    "filter", "first",
    "last", "reduce",
    "single", "joinToString"
)

val message = someKotlinCollectionFunctions.joinToString(
    separator = ", ",
    prefix = "Kotlin has many collection functions like: ",
    postfix = "and they are awesome.",
    limit = 3,
    truncated = "etc "
)

print(message) // Kotlin has many collection functions like: distinct, map, isEmpty, etc and they are awesome.

Transform a collection into a single result

If you want to transform a given collection into a single result, then you can use reduce function. For example, you can find the sum of all the elements present in a list:

val numList = listOf(1, 2, 3, 4, 5)
val result = numList.reduce { result, item ->
    result + item
}
print(result) // 15

// NOTE: If the list is empty, then it will throw a RuntimeException

Find if all elements are satisfying a particular condition

If you have an array or list of data elements and you want to find whether or not all the elements are satisfying a particular condition, then you can use all in Kotlin.

data class User(val id: Int, val name: String, val isCricketLover: Boolean, val isFootballLover: Boolean)

val user1 = User(id = 1, name = "Amit", isCricketLover = true, isFootballLover = true)
val user2 = User(id = 2, name = "Ali", isCricketLover = true, isFootballLover = true)
val user3 = User(id = 3, name = "Sumit", isCricketLover = true, isFootballLover = false)
val user4 = User(id = 4, name = "Himanshu", isCricketLover = true, isFootballLover = false)

val users = arrayOf(user1, user2, user3, user4)

val allLoveCricket = users.all { it.isCricketLover }
print(allLoveCricket) // true

val allLoveFootball = users.all { it.isFootballLover }
print(allLoveFootball) // false

Find a particular element based on a certain condition

You can find a particular element from a list of elements that is satisfying a particular condition by using find and single in Kotlin. For example, out of a list of students, you can find the student having roll number 5.

The find returns the first element matching the given condition or null if no such element was found.

While single returns the single element matching the given condition or it will throw an exception if there are more than one matching element or no matching element in the list.

data class User(val id: Int, val name: String)

val users = arrayOf(
    User(1, "Amit"),
    User(2, "Ali"),
    User(3, "Sumit"),
    User(4, "Himanshu")
)

val userWithId3 = users.single { it.id == 3 }
print(userWithId3) // User(id=3, name=Sumit)

val userWithId1 = users.find { it.id == 1 }
print(userWithId1) // User(id=1, name=Amit)

Break your list into multiple sublists of smaller size

There are many cases when you have a bigger list and you want to divide it into smaller parts and then perform some operation on those sublists. So, this can be easily achieved using the chunked function.

val numList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val chunkedLists = numList.chunked(3)
print(chunkedLists) // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Making copies of the array

You can make copies of your existing array by using various functions such as:

  • copyInto: This will replace the elements of one array into another array or it will through an exception if the destination array can't hold the elements of the original array due to size constraints or the indexes are out of bounds.
val arrayOne = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val arrayTwo = arrayOf(11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
arrayOne.copyInto(destination = arrayTwo, destinationOffset = 2, startIndex = 0, endIndex = 4)
arrayTwo.forEach {print("$it ")} // 11 12 1 2 3 4 17 18 19 20

Similarly, there are other functions that can be used to copy the elements of an array. For example:

  • copyOfRange(fromIndex, toIndex): Returns a new array which is a copy of the specified range of the original array.
  • copyOf() or copyOf(newSize): Returns a new array which is a copy of the original array, resized to the given newSize, or if the newSize is not passed then the whole array will be copied.

Changing type of collection to other

Depending on the situation, you can change the type of collection. Here, either you can change the type of one collection to another type by making a new collection or by referring to the older one. For example:

toIntArray, toBooleanArray, toLongArray, toShortArray, toByteArray, toDoubleArray, toList, toMap, toSet, toPair, etc can be used to change the type of one collection to another type.

var uIntArray = UIntArray(5) { 1U }
var intArray = uIntArray.toIntArray()
intArray[0] = 0
print(uIntArray.toList()) // [1, 1, 1, 1, 1]
print(intArray.toList()) // [0, 1, 1, 1, 1]

Here, we are making a new collection and changes in the new collection will not be reflected in the older one. But, at the same time, you can change the type of collection by keeping the reference to the older one i.e. changes in one collection will automatically be reflected in the other. For this instead of to, we need to use as. For example:

asIntArray, asLongArray, asShortArray, asByteArray, asList, etc.

var uIntArray = UIntArray(5) { 1U }
var intArray = uIntArray.asIntArray()
intArray[0] = 0
print(uIntArray.toList()) // [0, 1, 1, 1, 1]
print(intArray.toList()) // [0, 1, 1, 1, 1]

Associating the data using some key

If you are having a list of data and you want to associate the data with the help of some key present in your data element, then you can use associateBy.

data class Contact(val name: String, val phoneNumber: String)

val contactList = listOf(
    Contact("Amit", "+9199XXXX1111"),
    Contact("Ali", "+9199XXXX2222"),
    Contact("Himanshu", "+9199XXXX3333"),
    Contact("Sumit", "+9199XXXX4444")
)

val phoneNumberToContactMap = contactList.associateBy { it.phoneNumber }
print(phoneNumberToContactMap)
// Map with key: phoneNumber and value: Contact
// {
//     +9199XXXX1111=Contact(name=Amit, phoneNumber=+9199XXXX1111),
//     +9199XXXX2222=Contact(name=Ali, phoneNumber=+9199XXXX2222),
//     +9199XXXX3333=Contact(name=Himanshu, phoneNumber=+9199XXXX3333),
//     +9199XXXX4444=Contact(name=Sumit, phoneNumber=+9199XXXX4444)
// }

In the above example, the key is phoneNumber and the value is Contact. If you don't want to have the whole Contact as the value, then you can simply pass the desired value like this:

val phoneNumberToContactMap = contactList.associateBy({it.phoneNumber}, {it.name})
print(phoneNumberToContactMap)
// Map with key: phoneNumber and value: name
// {
//     +9199XXXX1111=Amit, 
//     +9199XXXX2222=Ali, 
//     +9199XXXX3333=Himanshu, 
//     +9199XXXX4444=Sumit}
// }

Finding distinct elements in a collection

We can use the distinct function to get the list of unique elements of a collection.

val list = listOf(1, 2, 2, 3, 3, 3, 4, 4, 4, 4)
println(list.distinct()) // [1, 2, 3, 4]

Union of collections

You can use the union function to get the unique elements of two collections. The order of the elements of both the collections will be preserved but the elements of the second collection will be added after the elements of the first collection.

val listOne = listOf(1, 2, 3, 3, 4, 5, 6)
val listTwo = listOf(2, 2, 4, 5, 6, 7, 8)
println(listOne.union(listTwo)) // [1, 2, 3, 4, 5, 6, 7, 8]

Intersection of collections

To get the elements that are common in two collections, you can use the intersect function which returns a set containing the common element of both collections.

val listOne = listOf(1, 2, 3, 3, 4, 5, 6)
val listTwo = listOf(2, 2, 4, 5, 6, 7, 8)
println(listOne.intersect(listTwo)) // [2, 4, 5, 6]

Keep the specified elements only

If in a collection, you want to keep the specified elements only then you can use retainAll function. Since this function will modify your list, so make sure that your list or array is mutable.

retainAll will return true if any element is removed from the collection otherwise it will return false.

val listOne = mutableListOf(1, 2, 3, 3, 4, 5, 6)
val listTwo = listOf(1, 2, 3, 3, 4, 5, 6)
val listThree = listOf(1, 2, 3, 3, 4, 5, 7)
println(listOne.retainAll(listTwo)) // false
println(listOne.retainAll(listThree)) // true
println(listOne) // [1, 2, 3, 3, 4, 5]

Similarly, you can use removeAll to remove all the elements of one collection that are present in another collection.

Filter a collection based on some condition

You can filter a collection based on certain conditions by using the filter. This returns a list containing elements that satisfy the given condition.

val list = listOf(1, 2, 3, 4, 5, 6, 7, 8)
val filteredList = list.filter { it % 2 == 0 }
print(filteredList) // [2, 4, 6, 8]

Similarly, you can filter the collection based on the index of elements by using filterIndexed.

If you want to store the filtered elements in some collection, then you can use the filterIndexedTo:

val list = listOf(1, 2, 3, 4, 5, 6, 7, 8)
val filteredList = mutableListOf<Int>()
list.filterIndexedTo(filteredList) { index, i -> list[index] % 2 == 0 }
print(filteredList) // [2, 4, 6, 8]

You can also find the elements that are instances of a specified type in a collection by using filterIsInstance.

val mixedList = listOf(1, 2, 3, "one", "two", 4, "three", "four", 5, 6, "five", 7)
val strList = mixedList.filterIsInstance<String>()
print(strList) // [one, two, three, four, five]

Zip collections

zip returns a list of pairs. The first element of the pair will be taken from the first collection and the second element of the pair will be taken from the second collection. The size of the returned list will be equal to the size of the shortest collection.

val listOne = listOf(1, 2, 3, 4, 5)
val listTwo = listOf("a", "b", "c", "d", "e", "f")
print(listOne zip listTwo) // [(1, a), (2, b), (3, c), (4, d), (5, e)]

Zip with next in a collection

zipWithNext return a list of pairs. The elements of the pair will be the adjacent elements of the collection.

val list = listOf(1, 2, 3, 4, 5)
print(list.zipWithNext()) // [(1, 2), (2, 3), (3, 4), (4, 5)]

Unzip a collection

unzip returns a pair of lists. The first list is made from the first elements of each pair and the second list is made from the second element of each pair.

val list = listOf("Amit" to 8, "Ali" to 10, "Sumit" to 4, "Himanshu" to 2)
val (players, footballSkills) = list.unzip()
print(players) // [Amit, Ali, Sumit, Himanshu]
print(footballSkills) // [8, 10, 4, 2]

Split array into two parts based on some condition

If you want to split your data into two parts based on some conditions like isFootballFan, then you can use partition.

data class User(val id: Int, val name: String, val isFootballLover: Boolean)

val users = listOf(
    User(1, "Amit", true),
    User(2, "Ali", true),
    User(3, "Sumit", false),
    User(4, "Himanshu", false)
)

val (footballLovers, nonFootballLovers) = users.partition { it.isFootballLover }
print(footballLovers) // [User(id=1, name=Amit, isFootballLover=true), User(id=2, name=Ali, isFootballLover=true)]
print(nonFootballLovers) // [User(id=3, name=Sumit, isFootballLover=false), User(id=4, name=Himanshu, isFootballLover=false)]

Reverse a list

You can reverse a list in Kotlin by using the reversed and asReversed function.

val list = listOf(1, 2, 3, 4, 5)
print(list.reversed()) // [5, 4, 3, 2, 1]
print(list.asReversed()) // [5, 4, 3, 2, 1]

Both are giving the same output but these functions are different.

The reversed() function can be applied on Array, List, and MutableList. It generates a new list that is the reverse of the original list.

But the asReversed() function can be applied on List and MutableList. It doesn't generate a new list because, after reversal, the new elements are still referring to the old one. So any change in one of them will result in a change in the other one.

Similarly, there are other functions that can be used to reverse the elements such as reversedArray(), reverse().

Group elements of a collection based on some condition

You can use groupBy() to group the elements of a collection based on certain conditions. For example, the below code will group the elements of the list based on the remainder when divided by 4 i.e. 4 groups will be there(when remainder = 0, 1, 2, and 3)

val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
print(list.groupBy { it % 4 })
// {
//     1=[1, 5, 9], 
//     2=[2, 6, 10], 
//     3=[3, 7], 
//     0=[4, 8]
// }

Sort element of a collection

You can sort the elements of a collection by using the sorted() function. This will return a sorted list.

val list = listOf(10, 4, 1, 3, 7, 2, 6)
print(list.sorted()) // [1, 2, 3, 4, 6, 7, 10]

Similarly, there are other functions that can be used to sort the collection based on certain conditions. Some of these functions are sortedArray, sortedArrayWith, sortedBy, sortedByDescending, sortedArraydescending, sortedWith, etc.

So, these are some of the collection functions that can be used while dealing with collections in Kotlin.

Hope you enjoyed this blog.

Keep Learning

Team MindOrks!

To get regular updates on Android Development, connect with us on Twitter, Linkedin, Github, and Facebook