Collections Operations - Part 1 : Transformation

Collections Operations - Part 1 : Transformation

·

7 min read

In this article , lets discuss about Kotlin Collections operation - transformation functions


Kotlin Transformation function builds a new collection based on the transformation rules passed in the function.

Map

  • Mapping transformation creates a collection from the result of function on the another collection's element

map()

  • applies the given function to each element and returns the list of lambda result

  • order of elements is same as order of original elements collection's order

 val numbers = setOf(1, 3, 2, 5, 4, 6, 7, 9, 0)
  // map
  println(numbers.map { it * 3 })
  //output ; [3, 9, 6, 15, 12, 18, 21, 27, 0]

mapIndexed()

  • Apply a map() transformation that additionally uses the element index as argument
   val numbers = setOf(1, 3, 2, 5, 4, 6, 7, 9, 0) 
   //map indexed
    println(numbers.mapIndexed { index: Int, i: Int -> "index:$index - value:$i" })
    // output : [index:0 - value:1, index:1 - value:3, index:2 - value:2, index:3 - value:5,
    // index:4 - value:4, index:5 - value:6, index:6 - value:7, index:7 - value:9,
    // index:8 - value:0]

mapNotNull() & mapNotNullIndexed()

  • If the transformation produces null on certain element , we can filter out null from result collection
    val numbers = setOf(1, 3, 2, 5, 4, 6, 7, 9, 0) 
    //map null
    println(numbers.mapNotNull { if (it == 5) null else it * 10 })
    // output : [10, 30, 20, 40, 60, 70, 90, 0]

    //map indexed null
    println(numbers.mapIndexedNotNull { index, i -> if (index == 4) null else i * 10 })
    // output : [10, 30, 20, 50, 60, 70, 90, 0]

When transforming maps , there are two options

  1. mapKeys() -> transform key leaving values unchanged

  2. mapValues() -> transform values leaving keys unchanged

💡
Both functionality takes map as an argument , so we can operate on both key and values
   val numbersMap = mapOf("Key1" to 1, "Key2" to 2,
                "Key3" to 3, "Key4" to 4, "Key5" to 5)

    //map keys and map values
    println(numbersMap.mapKeys { it.key.reversed() })
    // output : {1yeK=1, 2yeK=2, 3yeK=3, 4yeK=4, 5yeK=5}
    println(numbersMap.mapValues { "${it.value} + ${it.key.uppercase()}" })
    // output : {Key1=1 + KEY1, Key2=2 + KEY2, Key3=3 + KEY3, 
    // Key4=4 + KEY4, Key5=5 + KEY5}

Zip

Zipping transformation is building pairs from elements with same positions in two collections

zip()

  • returns the list of pair object

  • if the collection have different sizes , then the result of zip() is smaller size collection size and the result will not contain the larger collection's last element

    val studentName = listOf("stu 1", "stu 2", "stu 3", "stu 4")
    val studentAge = listOf(20, 35, 5, 0, 30)
    // zip 
    println(studentName zip studentAge)
    // output : [(stu 1, 20), (stu 2, 35), (stu 3, 5), (stu 4, 0)]
  • Zip with transformation function takes 2 parameters

    1.Receiver element

    2.Argument element

  • Transformation function called on pairs of receiver and argument element with same position

    val studentName = listOf("stu 1", "stu 2", "stu 3", "stu 4")
    val studentAge = listOf(20, 35, 5, 0, 30)
    //zip transformation
    println(studentName.zip(studentAge) { name, age ->
        "The Student name is $name amd the age is $age"
    })
    // output : [The Student name is stu 1 amd the age is 20, 
    //The Student name is stu 2 amd the age is 35,
    // The Student name is stu 3 amd the age is 5, 
    //The Student name is stu 4 amd the age is 0]

unzip()

We can do the reverse transformation on the list of pairs and the result is of two lists

1.First list contains first element of each pair

2.Second list contains second element

    // unzipping
    val studentInfo = listOf(
        "stu1" to 20,
        "stu2" to 35,
        "stu3" to 5,
        "st4" to 0
    )
    println(studentInfo.unzip())
    // output : ([stu1, stu2, stu3, st4], [20, 35, 5, 0])

Associate

  • Allow building maps from collection elements and values associated from them
💡
If the elements are equal , the last one remains in the result map

associateWith()

  • creates a map in which element of original collection are keys and values are result of given transformation function
    val numbers = listOf(1, 3, 5, 3, 7)

    // associateWith
    println(numbers.associateWith { it * 2 + 1 })
    // output : {1=2, 3=6, 5=10, 7=14}

associateBy()

  • creates a map with original collection as values

  • it takes a function that returns the key based on elements transformation function

    val numbers = listOf(1, 3, 5, 3, 7)

    // associateBy
    println(numbers.associateBy { it * 2 })
    //output :{2=1, 6=3, 10=5, 14=7}

associate()

  • For building maps where both keys and values are produced from the original collection elements

  • It takes a lambda function and returns a Pair: key and value of corresponding map entry

💡
associate() produces short living pair objects which may affect performance
    val numbers = listOf(1, 3, 5, 3, 7)
    // associate
    println(numbers.associate {
        it to (it * 2 + 3)
       }
    )
    //output : {1=5, 3=9, 5=13, 7=17}

Flatten

  • operates on nested collection that provide flat access to nested collection elements

flatten()

  • call it on the collection of elements (list of sets)

  • returns the list of elements in the nested collection

    val sampleSetList = listOf(
        setOf(3, 4, 5),
        setOf(0, 1, 2),
        setOf(7, 8, 9),
        setOf(6, 7, 8)
    )

    // flatten
    println(sampleSetList.flatten())
    //output : [3, 4, 5, 0, 1, 2, 7, 8, 9, 6, 7, 8]

flatMap()

  • flexible way to process nested collection

  • takes a function that maps a collection element to another collection

  • returns a single list on its return values on all elements

  • so basically flatMap() is a mixture of both map() and flatten()

    val containers = listOf(
        listOf("one", "two", "three"),
        listOf("four", "five", "six"),
        listOf("seven", "eight")
    )
   // flatmap
    println(containers.flatMap {
        it.asReversed()
    })
    // output : [three, two, one, six, five, four, eight, seven]

String Representation

  • If we need collection in a readable format , use the transform function like joinTostring() and joinTo()

joinToString()

  • builds a string string from the collection result based on provided argument
    val numbers = listOf("one", 1, "two", 2, "three", 3)

    // joinToString
    println(numbers.joinToString())
    // output : one, 1, two, 2, three, 3

joinTo()

  • builds a string from collection but appends the result to the given append able object

  • result is a string of collection elements separated by commas with spaces

    val numbers = listOf("one", 1, "two", 2, "three", 3)

    // joinTo
    val listContents = StringBuffer("The contents are -> ")
    numbers.joinTo(listContents)
    println(listContents)
    //output :The contents are -> one, 1, two, 2, three, 3

joinToString() - prefix, postfix and separator

  • To build a custom string representation , we can user the prefix, postfix and separator in the joinToString()
   val numbers = listOf("one", 1, "two", 2, "three", 3)

   // joinToString
    println(
        numbers.joinToString(
            separator = " -- ",
            prefix = "Begin: ",
            postfix = " :End"
        )
    )
    //output: Begin: one -- 1 -- two -- 2 -- three -- 3 :End

joinToString() - limit and truncate

  • This limit and truncate parameter is used in joinToString() for bigger collections , this will limit the elements in result

  • if the limit is reached , truncated argument will replace the rest of all elements in the collection

   // limit and truncate 
    val numbersList = (1..100).toList()
    println(
        numbersList.joinToString(
            limit = 12, truncated = " and so on"
        )
    )
    //output ; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,  and so on

joinToString() - transform

  • To customize the elements in collections , we can use transform function as follows
// transform
    val numbersString = listOf("one", "two", "three")
    println(numbersString.joinToString { 
         "Element -> ${it.reversed().uppercase()}" 
      }
    )
    //output: Element -> ENO, Element -> OWT, Element -> EERHT

Please leave your comments to improve.

Happy and Enjoy coding