kotlinmapsnullablenullable-reference-typesmutablelist

Filter a Map with mutableList values


I have the following map object :

var myStringMap = mapOf(10 to mutableListOf<String>(),11 to mutableListOf<String>(), 12 to mutableListOf<String>())

I want to append files from a source to the corresponding key as follows :

  myStringMap.keys.forEach { key ->
    getStringFromSource(source, user).let {
      if (it != null) {
        myStringMap[key]!!.add(it)
       }
    }
  }

The thing is that I need to add !! else the editor is complaining about a safe call for nullable object. Why is that ?

After that, when I want to filter the keys whose values are empty I have a typing error as long as GetBytes has MutableList? .

myStringMap.filter { (_: Int, value) -> value.isNotEmpty() }.let {
      it.keys.forEach { key ->
        val bytes = GetBytes(it[key])
        allBytes.add(bytes)
      }
}

Why is that? the it context should be Map<Int,MutableList>? Probably I should convert the mutableList to a list?


Solution

  • Why it[key] still returns a nullable type even when you pass in something from its keys collection? Well, because that is what the get method of Map is declared to return. It doesn't depend on the value you pass in.

    The key thing to realise here is that you are trying to do something to each of the values of the map, which are all mapped to a key. You are trying to add an element to each of the values, which are lists. You should access values, not keys.

    If getStringFromSource has side effects, e.g. returns a different string every time you call it, you can do:

    myStringMap.values.forEach { value ->
        getStringFromSource(source, user)?.let { value.add(it) }
    }
    

    If getStringFromSource does not have side effects, you can call it first:

    getStringFromSource(source, user)?.let { string ->
        myStringMap.values.forEach { it.add(string) }
    }
    

    If you actually meant getStringFromSource(source, key), then you should operate on entries, which gives you both the key and value to work with:

    myStringMap.entries.forEach { (key, value) ->
        getStringFromSource(source, key)?.let { value.add(it) }
    }
    

    This applies to the second situation too. It seems like you are trying to do something with each of the values, so just access the values, not the keys.

     myStringMap.filterValues(List<String>::isNotEmpty).values.forEach { 
          allBytes.add(GetBytes(it)) 
     }