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)