Accessing an attribute of a map Entry (Kotlin) - kotlin

I have the following class:
class Entry1(var type:String, var kind:String, var index:Int)
And a map:
var map1 = mutableMapOf<String, Entry>()
How would I access the attributes of Entry1 of a given map entry?
Example: Say I have:
map1["ex1"] = Entry("ex2","ex3",4)
Now I want to get the indexfrom the Entry I've added.
How can that be done?

val entry = map1["ex1"]
if (entry != null) {
val index = entry.index;
...
}

or
val entry = map1["ex1"]
val index = entry?.let{
entry.index
} ?: -1 //optional

Related

How i can update document in Firestore kotlin?

This image show my firestore documents which that auto generated.Which i want to do
//I want to update or merge Firestore document without adding new document. So i want to image url to firestore existing document field with merge method. But it does not overwrite, it is going to create a new document with auto-generated ID. How i can fix it?
val uuid = UUID.randomUUID()
val imageName = "$uuid.jpg"
val reference = storage.reference
val imagereference = reference.child("images").child(imageName)
if (selectedPicture != null) {
imagereference.putFile(selectedPicture!!).addOnSuccessListener {
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val uploadPictureReference = storage.reference.child("images").child(imageName)
uploadPictureReference.downloadUrl.addOnSuccessListener {
val downloadUrl = it.toString()
if (uid != null) {
val postMap = hashMapOf<String, Any>()
postMap.put("downloadUs", downloadUrl)
postMap.put("Email", auth.currentUser!!.email!!)
postMap.put("date", Timestamp.now())
val new = firestore.collection("ProfPic").document(uid)
new.set(postMap, SetOptions.merge()).addOnSuccessListener { }.
addOnFailureListener { Toast.makeText(this#MainActivity, it.localizedMessage, Toast
.LENGTH_LONG).show() }

How to transform a list to a map of lists in Kotlin?

I have a list :
field = ["key1.value1", "key1.value2", "key2.value3"]
that I want to transform into a Map<String, List<String>> such as
attributes = {"key1"=["value1", "value2"], "key2"=["value3"]}
The following works pretty well :
for (elem in field) {
val key = elem.split(".").first()
val value = elem.split(".").last()
if (key in attributes.keys) attributes[key]!!.add(value)
else {
attributes[key] = mutableListOf()
attributes[key]!!.add(value)
}
}
but it's not very kotlin-like. I tried with associateByTo:
val attributes = mutableMapOf<String, List<String>>()
field.associateByTo(
destination = attributes,
keySelector = { it.split(".").first() },
valueTransform = { mutableListOf(it.split(".").last()) }
)
but this only keeps the last value, as said in the doc. How can I build such a map in a kotlin way?
You can do this without intermediate collections by using groupBy directly:
val attributes = field.groupBy({ it.substringBefore(".") }, { it.substringAfter(".") })
Or to make it slightly more readable to the uninitiated:
val attributes = field.groupBy(
keySelector = { it.substringBefore(".") },
valueTransform = { it.substringAfter(".") },
)
You need groupBy function
val result = field
.map { it.split(".") }
.groupBy({ it.first() }, { it.last() })

How to explode a list into a Map in kotlin?

Imagine you want to transform this:
val initialValues: List<Pair<String, String>>
where the first String represents a key, the second a value
into a map:
val finalMap: Map<String,String>
containing each pair item twice, the first with the original key, the second one with a sort of expanded key.
How would you do that? Currently I'm using a
val finalMap = mutableMapOf<String, String>()
that I use while I'm iterating over initialValues. But I really don't like it.
initialValues.forEach {
val explodedPairs:List<Pair<String,String>> = <do-something>
explodedPairs.forEach { finalMap.put(it.first, it.second) }
}
how would you do more assertively?
You can use associate / associateBy like this -
val map1 = initialList.associate { it.first to it.second }
println(map1.toString()) //{1=x, 2=y}
val map2 = initialList.associateBy({it.first},{it.second})
println(map2.toString()) //{1=x, 2=y}
You can also use toMap and do this -
val map3 = initialList.toMap()
println(map3.toString()) //{1=x, 2=y}
where this is my initialList declaration -
val initialList = listOf(Pair(1, "x"), Pair(2, "y"))
You can use associate and associateby
You could use mapOf like this:
val initialValues: List<Pair<String, String>> = listOf()
val final = mapOf(*initialValues.toTypedArray())
But #Supriya has the better answer using toMap

Kotlin data class create dynamically json of its fields using GSON

I have a data class like this:
data class TestModel(
val id: Int,
val description: String,
val picture: String)
If I create JSON from this data class using GSON and it generates a result like this
{"id":1,"description":"Test", "picture": "picturePath"}
What to do if I need the following JSON from my data class:
{"id":1, "description":"Test"}
And other times:
`{"id":1, "picture": "picturePath"}
`
Thanks in advance!
You can solve this problem with writing custom adapter and with optional types:
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
data class TestModel(
val id: Int,
val description: String? = "",
val picture: String? = "")
class TesModelTypeAdapter : TypeAdapter<TestModel>() {
override fun read(reader: JsonReader?): TestModel {
var id: Int? = null
var picture: String? = null
var description: String? = null
reader?.beginObject()
while (reader?.hasNext() == true) {
val name = reader.nextName()
if (reader.peek() == JsonToken.NULL) {
reader.nextNull()
continue
}
when (name) {
"id" -> id = reader.nextInt()
"picture" -> picture = reader.nextString()
"description" -> description = reader.nextString()
}
}
reader?.endObject()
return when {
!picture.isNullOrBlank() && description.isNullOrBlank() -> TestModel(id = id ?: 0, picture = picture)
!description.isNullOrBlank() && picture.isNullOrBlank() -> TestModel(id = id ?: 0, description = description)
else -> TestModel(id ?: 0, picture, description)
}
}
override fun write(out: JsonWriter?, value: TestModel?) {
out?.apply {
beginObject()
value?.let {
when {
!it.picture.isNullOrBlank() && it.description.isNullOrBlank() -> {
name("id").value(it.id)
name("picture").value(it.picture)
}
!it.description.isNullOrBlank() && it.picture.isNullOrBlank() -> {
name("id").value(it.id)
name("description").value(it.description)
}
else -> {
name("id").value(it.id)
name("picture").value(it.picture)
name("description").value(it.description)
}
}
}
endObject()
}
}
}
class App {
companion object {
#JvmStatic fun main(args: Array<String>) {
val tm = TestModel(12, description = "Hello desc")
val tm2 = TestModel(23, picture = "https://www.pexels.com/photo/daylight-forest-glossy-lake-443446/")
val tm3 = TestModel(12, "Hello desc", "https://www.pexels.com/photo/daylight-forest-glossy-lake-443446/")
val gson = GsonBuilder().registerTypeAdapter(TestModel::class.java, TesModelTypeAdapter()).create()
System.out.println(gson.toJson(tm))
System.out.println(gson.toJson(tm2))
System.out.println(gson.toJson(tm3))
}
}
}
Here is actually a way to ignore fields, that are not marked via #Exposed annotation. In order for this to work, special configuration should be used when instantiating Gson. Here is how you can to this.
Easy way is to mark the field as #Transient. Then it would not be either serialized and deserialized.
I want to give you alternative ways without manually serialization/deserialization.
data class TestModel(
val id: Int,
val description: String? = null,
val picture: String? = null)
When you create json from data class
val params = TestModel(id = 1, description = "custom text")
or
val params = TestModel(id = 1, picture = "picture path")
If one of them field is null of data class GSON skips that field
automatically.

Check for null in map function in Kotlin

I'm new to Kotlin and I want to map an object (ProductVisibility) base on another one (fmpProduct). Some object can't be converted so I need to skip them on some condition.
I wanted to know if there's a better way to do this than what I did with the filter and the "!!" I feel that it's hacked. Am I missing something ?
val newCSProductVisibility = fmpProducts
.filter { parentIdGroupedByCode.containsKey(it.id) }
.filter { ProductType.fromCode(it.type) != null } //voir si on accumule les erreus dans une variable à montrer
.map {
val type = ProductType.fromCode(it.type)!! //Null already filtered
val userGroupIds = type.productAvailabilityUserGroup.map { it.id }.joinToString(",")
val b2bGroupIds = type.b2bUserGroup.map { it.id }.joinToString { "," }
val b2bDescHide = !type.b2bUserGroup.isEmpty()
val parentId = parentIdGroupedByCode[it.id]!! //Null already filtered
CSProductDao.ProductVisibility(parentId, userGroupIds, b2bGroupIds, b2bDescHide)
}
edit: updated the map access like comment suggested
Use mapNotNull() to avoid the filter()s and do everything in the mapNotNull() block, then the automatic typecast to non-null type works.
Example:
fun f() {
val list = listOf<MyClass>()
val v = list.mapNotNull {
if (it.type == null) return#mapNotNull null
val type = productTypeFromCode(it.type)
if (type == null) return#mapNotNull null
else MyClass2(type) // type is automatically casted to type!! here
}
}
fun productTypeFromCode(code: String): String? {
return null
}
class MyClass(val type: String?, val id: String)
class MyClass2(val type: String)