Check if array contains substring? - kotlin

I've got this array:
val array : MutableList<String> = mutableListOf("laptop on floor", "man is thomas")
How do I check if it contains this string: flo? (It does, in the first element)

Try this code snippet
val array : MutableList<String> = mutableListOf("laptop on floor", "man is thomas")
val doesContain1 = array.indexOfFirst { it.contains("flo") } >= 0
// or
val doesContain2 = array.find { it.contains("flo") } != null
// or
val doesContain3 = array.any { it.contains("flo") }

Related

MutableLiveData, LiveData List empty or add item first position

I don't quite understand the behavior of MutableLiveData or I can't find the proper documentation.
But I need help.
I need to add an element to the first position of the array and tried with "plus" but it doesn't work properly. Or is there a way to add multiple elements of an array in the first position?
_series.value = _series.value?.plus(it) // ??? 11111111
I can't figure out how to delete all the elements of the array, I did it with
_series.value = null // ??? 2222222
_series.value = ArrayList() /// ??? 2222222
but I don't think it is the best way to do it.
class SerieViewModel(val database: SerieDatabaseDao) : ViewModel() {
private val _series = MutableLiveData<List<Serie>?>()
val series: LiveData<List<Serie>?>
get() = _series
fun fetchSeries(code: String) {
viewModelScope.launch {
try {
val response = CodesApi.retrofitService.getSeries(code)
val series = response.body()?.data
series?.forEach {
_series.value = _series.value?.plus(it) // ??? 11111111
}
}
} catch (e: Exception) {
Log.d(TAG, "fetchSeries: ${e.message}")
}
}
}
fun clearDb() = viewModelScope.launch {
database.clear()
_series.value = null // ??? 2222222
_series.value = ArrayList<Serie>() /// ??? 2222222
}
thanks
You are storing a List<Serie?> which is fine thats what you should do, but you can't add elements to List, you need to convert List to ArrayList and add elements to the ArrayList than update the MutableLiveData, follow the code below.
to clear all the elements from the List you can set the value to emptyList(), follow the code below.
Note: Take a look about the difference between List, ArrayList, Array in kotlin they are not the same
class SerieViewModel(val database: SerieDatabaseDao) : ViewModel() {
private val _series = MutableLiveData<List<Serie>?>()
val series: LiveData<List<Serie>?>
get() = _series
fun fetchSeries(code: String) {
viewModelScope.launch {
try {
val response = CodesApi.retrofitService.getSeries(code)
val series = response.body()?.data
val liveDataSeries =
if (_series.value == null) emptyList<Serie>() // set the array list to empty if _series.value is null
else ArrayList(_series.value) // else: add _series.value elements to the array list
series?.forEach {
liveDataSeries.add(it) // add each serie to the array list
}
_series.value = liveDataSeries // update _series.value
} catch (e: Exception) {
Log.d(TAG, "fetchSeries: ${e.message}")
}
}
}
fun clearDb() = viewModelScope.launch {
database.clear()
_series.value = emptyList()
}
}

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() })

Kotlin: Find the first line of a file that matches a regex, and return captured values

i want get text from a file by using regEx and want save the file with a new name (using the results of the regEx-Find).
My Problem is that i cant get/return the correct genearated (in this example xyz maur) out of the function readFileLineByLineUsingForEachLine(fileName: String) the new newFileName which was generated (sucessfully as expected) in the function.
Line 1 of Source:
start {"Name":"xyz","Civ":"maur","Team":0}
My Prototype:
fun main() {
val f = "./commands.txt";
var newFileName = readFileLineByLineUsingForEachLine(f)
print(newFileName.)
val source = Paths.get(f)
val target = Paths.get("/home/x/snap/0ad/199/.local/share/0ad/replays/0.0.24/2021-03-14_0016/" + newFileName)
// try {
// val move = Files.move(
// source,
// target
// )
// } catch (e: IOException) {
// e.printStackTrace()
// }
};
fun readFileLineByLineUsingForEachLine(fileName: String) // https://www.baeldung.com/kotlin/read-file
= File(fileName).forEachLine lit#{
// "Name":"Cleisthenes"
val regexString = """
"Name":(?<Name>"\w+").*?"Civ":(?<Civ>"\w+").*?"Team":0
""".trim()
var regex = Regex(regexString)
var matched = regex.find(it)?.groupValues
val Name = matched?.get(1)
val Civ = matched?.get(2)
if (Name != null)
println(Name)
if (Civ != null)
println(Civ)
val newFileName = "$Name $Civ"
return#lit
}
Because you want to stop processing as soon as you find a match, I don't think forEachLine is the best choice. Instead you can use useLines, and combine it with first to stop processing once you get a match:
val regex = Regex(""""Name":(?<Name>"\w+").*?"Civ":(?<Civ>"\w+").*?"Team":0""")
fun readFileLineByLineUsingForEachLine(fileName: String) =
File(fileName).useLines { lines ->
val (name, civ) = lines
.map { regex.find(it) }
.filterNotNull()
.first()
.destructured
"$name $civ"
}
For the example you provided, this returns the string "xyz" "maur".
that's just a very little modification of the correct, helpful answer from Adam here https://stackoverflow.com/a/66654710/2891692
fun readFileLineByLineUsingForEachLine2(fileName: String) =
File(fileName).useLines { lines ->
val (name, civ) = lines
.map {
val regexString = """
"Name":(?<Name>"\w+").*?"Civ":(?<Civ>"\w+").*?"Team":0
""".trim()
var regex = Regex(regexString)
regex.find(it)
}
.filterNotNull()
.first()
.destructured
"$name $civ"
}

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)