Advanced Functions



Filter

filter(){} does the filtering. Filtering is one of the most popular tasks in collection processing. Filter is a condition element. These functions leave the original collection unchanged, so they are available for both mutable and read-only collections.


	
fun main(args: Array) {

val list = listOf(1,4,6,7,9)
									
val selectedList = list.filter { number -> number < 5 } // {it<5}
									
for(number in selectedList){
println(number)
}
									
}			

Output: 
1
4



MAP

Unlike filter, map changes the values of the list.


fun main(args: Array) {

	val list = listOf(1,2,4,5,7,8,9)
									
	val mapList = list.map { it+it }
									
	for (map in mapList){
	println(map)
	}					
}


Output:
2
4
8
10
14
16
18
  • For map operations, it's better to define lists as val because they are not modified.



  • filter and map combination

    Filter and Map can be used together.

    
    fun main(args: Array) {
    
    	val person = listOf(
    	People("Attila Hun",23,"Male"),
    	People("Khaleesi the queen <3",24,"Female"),
    	People("Homelander",33,"Transgender"),
    	People("Sauron",323,"Genderqueer"),
    	People("Galadriel",250,"Cisgender"),
    	People("Gandalf",273,"Genderfluid")
    	)
    									
    	val listCombine = person.filter{pers -> pers.age> 240}
    		.map { perso -> perso.gender }
    									
    	for(i in listCombine){
    	println(i)
    	}
    									
    }
    
    data class People(val name: String, val age: Int, val gender: String)
    
    
    
    Output:
    Genderqueer
    Cisgender
    Genderfluid
    
  • Filter and map are generally used when getting data from API. filtered, formatted and transformed into the desired format. The goal is to prepare objects.



  • let

    "let" takes the object it is invoked upon as the parameter and returns the result of the lambda expression. Kotlin let is a scoping function wherein the variables declared inside the expression cannot be used outside. let is usually used for nullable types.

    
    private var numb: Int? = null  
    
    	fun main(args: Array) {
    									
    		numb?.let {
    		val numb2 =it+1    //using val over var is preferable because the variable defined cannot be used outside the scope of let. Using val is more correct in terms of its structure.
    		println(numb2)
    		}
    									
    }
    
    
    Output:
    //Empty
    	
    



    sample II

    
    private var numb: Int? = 8
    
    	fun main(args: Array) {
    									
    		numb?.let {
    		val numb2 =it+1
    		println(numb2)
    		}
    					
    	}
    
    
    Output:
    9
    
  • "let" can be used when getting unpredictable data from the API.



  • sample III

    It would be good to put a question mark before the .let, otherwise it may be necessary to use it in the steps inside. Since ". let" is a method based on working with null, it is better to put the question mark at the beginning.

    
    private var numb: Int? = 8
    
    	fun main(args: Array) {
    										
    	val num = numb?.let { value ->
    	value + 1
    	}
    	
    	println(num)
    	}
    
    
    Output:
    9
    

    - Lambda usage is shown in the above code.




    let and null value

    
    private var numb: Int? = null
    
    	fun main(args: Array) {
    									
    		val num = numb?.let { value ->
    		value + 1
    		} ?:0
    										
    	println(num)
    									
    	}
    
    
    Output:
    0
    
  • if let{} ?:0 is set to null then the value can be set to zero.



  • also

    With "also", things to do can be placed between the corresponding bracket. Multiple operations can be performed in a single line.

    
    
    fun main(args: Array) {
    
    	val ati = Person("Ati",31)
    	val leo = Person("Leo",64)
    	val shiv = Person("Shiv",21)
    									
    	val people = listOf(ati,leo,shiv)
    									
    	people.filter { it.age>28 }.also { 
    		for (per in it){
    		println(per.name)
    		}
    	}
    }
    									
    data class Person (var name:String, var age:Int)				
    
    
    Output:
    Ati
    Leo
    



    apply

    Kotlin apply is an extension function on a type. It runs on the object reference (receiver) into the expression and returns the object reference on completion.

    
    fun main(args: Array) {
    
    	val person = Person("ati",24)
    										
    	person.apply { this.age = 13 }
    		println(person)
    }
    									
    data class Person(var name: String, var age : Int)					
    
    
    Output:
    Person(name=ati, age=13)
    



    apply and also

    apply and also can be used together.

    
    fun main(args: Array) {
    
    	Person("ari",12).apply {
    		name = "ARI"
    	}.also {
    		println(it.name)
    	}
    										
    }
    									
    data class Person(var name: String, var age : Int)
    
    
    Output:
    ARI
    



    apply sample II

    
    fun main(args: Array) {
    
    	val person = Person("ari",12).apply {
    		name = "ARI"
    	}
    	
    	println(person.name)
    										
    }
    									
    data class Person(var name: String, var age : Int)
    
    
    Output:
    ARI
    



    run

    Run method is very important for nullable safety.

    
    class ClassRoom(){
        lateinit var names: String
        lateinit var teachers: String
        lateinit var schools: String
    }
    
    fun main(){
       println("ClassRoom name: ")
       var classRoom: ClassRoom? = null
    
        classRoom?.run {
            print(names)     //Not print on the screen cause object is null.
        }
    
        print("ClassRoom name: ")
    
        classRoom = ClassRoom().apply {
            names = "Merve Grealish"
            teachers = "Mete Cengiz namıtürk"
            schools = "Atatürk University"
        }
    
        classRoom.run { //successful.
            print(names)
        }
    
    }
    
    
    Output:
    ClassRoom name: 
    ClassRoom name: Merve Grealish
    

    When the classRoom value is null, the body of the run is simply ignored. When it is non-null, the body executes.




    with

    The class/object is put inside the with function. Since this function returns unit type it might be better to put the variable.

    
    fun main(args: Array) {
    
    	val pers = with(Person("Ari",13)){
    		name = "ARI"
    	}
    									
    	println(pers.name)  // unit type x. no return.
    									
    }
    									
    data class Person(var name: String, var age : Int)
    
    
    Output:
    //unit type
    



    sample II

    
    fun main(args: Array) {
        
        val pers = Person("Ari",13)
    
        with(pers){
            name = "ARI"
            age = 43
        }
    
        println(pers.name + " " + pers.age)
    
    }
    
    data class Person(var name: String, var age : Int)
    
    
    
    Output:
    ARI 43
    



    Summary

  • let: working with nullable objects to avoid NullPointerException.
  • apply: changing object configuration.
  • run: operate on nullable object, executing lambda expressions.
  • also: adding additional operations.
  • with: operating on non-null objects.



  • Function selection


    Function Object reference Return value Is extension function
    let it Lambda Result Yes
    run this Lambda Result Yes
    run - Lambda Result No: called without the context object
    with this Lambda Result No: takes the context object as an argument.
    apply this Context object Yes
    also it Context object Yes



    -> Some good resources on the subject:

  • kotlinlang.org
  • digitalocean
  • geeksforgeeks