How can I convet MutableMap<String, Any?> to vararg values: Pair<String, Any?>?
Asked Answered
P

2

8

The following code is from "Kotlin for Android Developers", you can access it at https://github.com/antoniolg/Kotlin-for-Android-Developers

In order to insert a row data into CityForecastTable.NAME, I have to pass a vararg values by Source Code fun SQLiteDatabase.insert

1: The author make a extension toVarargArray() to convert a MutableMap<String, Any?> to Pair<String, Any?>, I don't know whether there is a better way to do that. Do I need to use a extension function?

2: The author have to use the code it.value!! in Code C , I don't know if the code fun <K, V : Any> Map<K, V?>.toVarargArray(): Array<out Pair<K, V?>> = map({ Pair(it.key, it.value) }).toTypedArray() is right?

3: I don't know whether the app will crash when I insert a row data of which a column include null value.

import org.jetbrains.anko.db.*
class ForecastDb(private val forecastDbHelper: ForecastDbHelper = ForecastDbHelper.instance,
                 private val dataMapper: DbDataMapper = DbDataMapper()) : ForecastDataSource {

    fun saveForecast(forecast: ForecastList) = forecastDbHelper.use {           

         //dataMapper.convertFromDomain(forecast) will return CityForecast object 
         with(dataMapper.convertFromDomain(forecast)) {
            insert(CityForecastTable.NAME, *map.toVarargArray())           
        }
    }

}


class CityForecast(val map: MutableMap<String, Any?>, val dailyForecast: List<DayForecast>) {
    var _id: Long by map
    var city: String by map
    var country: String by map

    constructor(id: Long, city: String, country: String, dailyForecast: List<DayForecast>)
            : this(HashMap(), dailyForecast) {
        this._id = id
        this.city = city
        this.country = country
    }
}

Code C

fun <K, V : Any> Map<K, V?>.toVarargArray(): Array<out Pair<K, V>> =
        map({ Pair(it.key, it.value!!) }).toTypedArray()

Source Code

fun SQLiteDatabase.insert(tableName: String, vararg values: Pair<String, Any?>): Long {
    return insert(tableName, null, values.toContentValues())
}
Personnel answered 22/7, 2018 at 1:59 Comment(1)
Isn't quite easy to test if the app will crash?Bagging
D
3

You have pretty much everything there

Use the asterisk * operator also known as spread operator to spread your array to vararg inside function.

https://kotlinlang.org/docs/reference/functions.html#variable-number-of-arguments-varargs

Add ? to the value and you are ready to go

fun <K, V : Any> Map<K, V?>.toVarargArray() = map { it.key to it.value }.toTypedArray()

    insert("", *mapOf("k" to "v").toVarargArray())
Dina answered 22/7, 2018 at 5:4 Comment(3)
Thanks! Is the code fun <K, V : Any> Map<K, V?>.toVarargArray(): Array<out Pair<K, V?>> = map({ Pair(it.key, it.value) }).toTypedArray() right?Personnel
I don't know whether the app will crash when I insert a row data of which a column include null value.Personnel
Thanks! will the code crash if V? is null to invoke it.value at fun <K, V : Any> Map<K, V?>.toVarargArray() = map { it.key to it.value }.toTypedArray() ?Personnel
C
2
  1. No, you do not really need an extension function for that. It may make sense to have something in place if you are calling the insert-method (or any other which requires arrays instead of a Map) often. However depending on what you call more often, you may rather supply an extension function for the specific method with the Map as parameter instead.

    If you do not want to have the extension function you can call the following directly (if you aleady have a map):

    *map.map { it.key to it.value }.toTypedArray()
    
  2. I wonder why the author allows Any? on the CityForecast-class as values but then decides to use value!!. I would rather recommend either using Any, that way no null values are allowed or otherwise falling back to a default, e.g. value?:"". Note that if you use Any, you probably need to ensure that your data in the database is not containing any null-values (column created with NOT NULL constraint).

  3. You really should try it out. If it crashes you get familiar on how to deal with errors. I assume that it crashes (or at least throws a NullPointerException somewhere ;-)) if you use null values in the map and keep the mentioned extension function as then the value!!-call will fail. And note: you can always come back here to ask new questions, if there is an issue you can't resolve or you do not find any good sources for it.

Chokecherry answered 24/7, 2018 at 14:42 Comment(5)
Thanks! For 1, could you show me some code instead of *map.toVarargArray() in insert(CityForecastTable.NAME, *map.toVarargArray()) ?Personnel
Basically you can just call what is written in the extension function. Note that I do not know the method signature of insert, but I think the following will already suffice: insert(CityForecastTable.NAME, "k" to "v", "otherkey" to "othervalue", /*alternatively*/ Pair("k2", "v2"))Chokecherry
Ok. I know the signature if it's the one you posted, but with the mobile writing some comments it just disappears ;-) so the mentioned code in the comment suffices already.Chokecherry
Thank! I hope to convet MutableMap<String, Any?> to vararg values: Pair<String, Any?> as parameter of insert function. At present, I think that extension function fun <K, V : Any> Map<K, V?>.toVarargArray(): Array<out Pair<K, V>> = map({ Pair(it.key, it.value!!) }).toTypedArray() is not good.Personnel
Well, it's nearly ok. I don't trust the it.value!!. Why shouldn't I be allowed to insert null-values (except the database column has NOT NULL declared). If you don't want to use the extension function, but you want to use the map, then you can still write: *map.map { it.key to it.value }.toTypedArray().Chokecherry

© 2022 - 2024 — McMap. All rights reserved.