Home > Software engineering >  run function in Kotlin
run function in Kotlin

Time:08-04

I have a question abount run function.

I totally understand The last 'eachCount()' result, but run function give a bit punch for me.

How does it works?

id_list: ["muzi", "frodo", "apeach", "neo"]

report: ["muzi frodo","apeach frodo","frodo neo","muzi neo","apeach muzi"]

k: 2

Answer: [2, 1, 1, 0]

class Solution {
fun solution(id_list: Array<String>, report: Array<String>, k: Int): IntArray =
report.map { it.split(" ") }// [[muzi, frodo], [apeach, frodo], [frodo, neo], [muzi, neo], [apeach, muzi]]

    .groupBy { it[1] }//    {frodo=[[muzi, frodo], [apeach, frodo]], neo=[[frodo, neo], [muzi, neo]], muzi=[[apeach, muzi]]}

    .asSequence()
    .map { it.value.distinct() }//[[[muzi, frodo], [apeach, frodo]], [[frodo, neo], [muzi, neo]], [[apeach, muzi]]]

    .filter { it.size >= k }//  [[[muzi, frodo], [apeach, frodo]], [[frodo, neo], [muzi, neo]]]

    .flatten()//    [[muzi, frodo], [apeach, frodo], [frodo, neo], [muzi, neo]]

    .map { it[0] }//[muzi, apeach, frodo, muzi]

    .groupingBy { it }
    .eachCount()//  {muzi=2, apeach=1, frodo=1} -> run function's reciver object??
    .run { id_list.map { getOrDefault(it, 0)}.toIntArray() } -> I don`t know how it works.

}

CodePudding user response:

The last line is equivalent to

.run { id_list.map { this.getOrDefault(it, 0) }.toIntArray() } 

where this is the map returned by .eachCount() on the previous line and it is each element of id_list.

CodePudding user response:

thing.run puts you inside the scope of thing, as if you're running code inside its class, and inside the function block, this is thing. Usually that's for convenience, so you don't have to keep referring to the object explicitly:

// also useful for null-checking stuff before you try to do things with it!
thing?.run {
    doThing()
    println(someProperty)
}

let is similar, but instead of the object you're calling it on becoming a receiver (this in the function), it's passed in as a parameter instead:

thing?.let {
    // now you have to explicitly refer to the parameter
    it.doThing()
    println(it.someProperty)
}

There are similar pairs of receiver/parameter scope functions that do similar things, like apply/also. Which one you pick depends on what makes most sense. If you're calling things with the object as a parameter, it might feel more correct to call doThing(it) (or a renamed parameter that reads better!) than doThing(this)


The important thing in your bit of code is that the last line uses run and let. That allows it to refer to two different objects at that stage in the chain - the result of all the stuff that happened before (which has run called on it, so it becomes the receiver, this) and the current item in the map call, it.

You're basically calling all that stuff and producing a Map. Then you call run, which is going to transform that Map into another value, and gives you access to the Map through this. Then you produce a value to output by calling map on your id_list and doing a lookup on your Map, through this.getOrDefault (which you can shorten to just getOrDefault).

It's basically the equivalent to this:

val myMap = // big chain of operations
val lookup = id_list.map { myMap.getOrDefault(it, 0) }.toIntArray()
return lookup

only run works as part of that big chain, transforming one value to another without declaring intermediate variables.

Btw you could use let if you wanted, so there are two parameters involved - one from the let function, one from the map function:

// giving it a name because you can't have two things called 'it'
.let { myMap -> id_list.map { myMap.getOrDefault(it, 0)}.toIntArray() }

but that's probably not an improvement, right? You can do it if you prefer it though, or want to be more explicit! (Being able to name the parameters can be a great way to document exactly what's happening - it might not be clear what the thing you're calling run or let on actually is, and a name can help)

  • Related