I'm practicing leetcode problems to perfect my kotlin syntax and am wondering why this code doesn't work. My question specifically is why doesn't my courses hashmap populate with this code.
Prerequisites is an array in this form [[0,1][0,3][4,5][6,7]] and if I print my variables for pre and post they print what I expect
But I'm trying to turn courses into an adjacency matrix like this {0: [1,3], 4: [5], 6: [7]} and instead it just prints an empty set every time
class Solution {
fun canFinish(numCourses: Int, prerequisites: Array<IntArray>): Boolean {
val courses = HashMap<Int, MutableList<Int>>().withDefault{ mutableListOf<Int>() }
for ((pre, post) in prerequisites){
courses[pre]?.add(post)
}
print(courses)
return false
}
}
stdout: {}
CodePudding user response:
[]
does not give you the default value
From the docs of withDefault
:
This implicit default value is used when the original map doesn't contain a value for the key specified and a value is obtained with Map.getValue function
If you want to get the default value, you need to use getValue
instead of the index operator.
Using the index operator, you would just get null
and because of the the null-safe operator, the add
operation would not even be executed.
If you take a look at the relevant source code, you can see that the funxtionality get
is not changed when using .withDefault
but only getOrImplicitDefault
returns the default value.
Getting the default does not set anything
Furthermore, when accessing courses.getValue(pre)
in the loop, the Map
will be empty. Because of the withDefault
, it will return a MutableList
where you can add elements but getting such a list and adding elements to it will not add the list to the Map
. Reading and accessing an element does not insert it.
Simple solution
If you want to make sure the element is present in the Map
, you can use courses[pre]=course.getValue(pre)
before reading courses[pre]?
:
class Solution {
fun canFinish(numCourses: Int, prerequisites: Array<IntArray>): Boolean {
val courses = HashMap<Int, MutableList<Int>>().withDefault{ mutableListOf<Int>() }
for ((pre, post) in prerequisites){
courses[pre] = courses.getValue(pre)
courses[pre]?.add(post)
}
print(courses)
return false
}
}
If the entry is set already, it will be set to itself (no change) and if it isn't set, it will be set to the default value (empty list).
CodePudding user response:
dan1st's answer covers it - your default list is just returned, not put and returned, so it's not part of the map - but here's a different take to get that functionality:
val courses = HashMap<Int, MutableList<Int>>().run {
withDefault{ key ->
mutableListOf<Int>().also { put(key, it) }
}
}
So basically using the withDefault
wrapper, using run
so the map is this
in the default value function, so you can add your list to the map before returning it. Then when you call courses.getValue(69)
you'll get back a list that's already been inserted into the map
If you like, there's also a function that'll do this grouping for you, groupBy
val nums = arrayOf(
intArrayOf(0,1),
intArrayOf(0,3),
intArrayOf(4,5),
intArrayOf(6,7)
)
val groups = nums.groupBy(keySelector = { it[0] }, valueTransform = { it[1] })
println(groups)
>> {0=[1, 3], 4=[5], 6=[7]}