How to resort a mutableStateOf(listOf<PojoObject>())? - kotlin

I have a populated:
data class PojoObject(val id: Int, val name: String)
val listData = mutableStateOf(listOf<PojoObject>())
//..
listData.value = anotherList.map{//..}
Assuming listData is already sorted by increasing id.
How can I resort the listData object by lets say name in alphabetic order?
I would prefer to operate on listItem directly without wrapping it like:
listData.value = listData.component1().map{//..}.sort{//..}

Related

Convert String into list of Pairs: Kotlin

Is there an easier approach to convert an Intellij IDEA environment variable into a list of Tuples?
My environment variable for Intellij is
GROCERY_LIST=[("egg", "dairy"),("chicken", "meat"),("apple", "fruit")]
The environment variable gets accessed into Kotlin file as String.
val g_list = System.getenv("GROCERY_LIST")
Ideally I'd like to iterate over g_list, first element being ("egg", "dairy") and so on.
And then ("egg", "dairy") is a tuple/pair
I have tried to split g_list by comma that's NOT inside quotes i.e
val splitted_list = g_list.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*\$)".toRegex()).toTypedArray()
this gives me first element as [("egg", second element as "dairy")] and so on.
Also created a data class and tried to map the string into data class using jacksonObjectMapper following this link:
val mapper = jacksonObjectMapper()
val g_list = System.getenv("GROCERY_LIST")
val myList: List<Shopping> = mapper.readValue(g_list)
data class Shopping(val a: String, val b: String)
You can create a regular expression to match all strings in your environmental variable.
Regex::findAll()
Then loop through the strings while creating a list of Shopping objects.
// Raw data set.
val groceryList: String = "[(\"egg\", \"dairy\"),(\"chicken\", \"meat\"),(\"apple\", \"fruit\")]"
// Build regular expression.
val regex = Regex("\"([\\s\\S]+?)\"")
val matchResult = regex.findAll(groceryList)
val iterator = matchResult.iterator()
// Create a List of `Shopping` objects.
var first: String = "";
var second: String = "";
val shoppingList = mutableListOf<Shopping>()
var i = 0;
while (iterator.hasNext()) {
val value = iterator.next().value;
if (i % 2 == 0) {
first = value;
} else {
second = value;
shoppingList.add(Shopping(first, second))
first = ""
second = ""
}
i++
}
// Print Shopping List.
for (s in shoppingList) {
println(s)
}
// Output.
/*
Shopping(a="egg", b="dairy")
Shopping(a="chicken", b="meat")
Shopping(a="apple", b="fruit")
*/
data class Shopping(val a: String, val b: String)
Never a good idea to use regex to match parenthesis.
I would suggest a step-by-step approach:
You could first match the name and the value by
(\w+)=(.*)
There you get the name in group 1 and the value in group 2 without caring about any subsequent = characters that might appear in the value.
If you then want to split the value, I would get rid of start and end parenthesis first by matching by
(?<=\[\().*(?=\)\])
(or simply cut off the first and last two characters of the string, if it is always given it starts with [( and ends in )])
Then get the single list entries from splitting by
\),\(
(take care that the split operation also takes a regex, so you have to escape it)
And for each list entry you could split that simply by
,\s*
or, if you want the quote character to be removed, use a match with
\"(.*)\",\s*\"(.*)\"
where group 1 contains the key (left of equals sign) and group 2 the value (right of equals sign)

Values loss for copy() for Kotlin data class

I have such data class:
data class BookObject(
val description: String?,
val owningCompanyData: OwningCompanyData?,
) {
var id: String? = null
var createdAt: Instant? = null
var createdBy: String? = null
var modifiedAt: Instant? = null
fun update(command: CreateOrUpdateBookObjectCommand): BookObject =
this.copy(
description = command.description,
owningCompanyData = command.owningCompanyData
)
}
When I use the update function for an object with completely filled fields, I get an object with empty id, createdAt, createdBy, modifiedAt fields (they become equal to null). But why is this happening? Why do these fields lose their values?
The kotlin documentation says:
Use the copy() function to copy an object, allowing you to alter some
of its properties while keeping the rest unchanged.
The answer actually is present in your link, located in the paragraph just before "Copying".
The compiler only uses the properties defined inside the primary constructor for the automatically generated functions.

Combining Two List in Kotlin with Index

There is a data class as fruits.
data class Fruits(
val code: String, //Unique
val name: String
)
The base list indexed items with boolean variable is as below.
val indexList: MutableList<Boolean> = MutableList(baseFruitList.size) { false }
Now the Favourite Indexed list is as below
val favList: MutableList<Boolean> = MutableList(favFruitList.size) { true}
I want a combined full list which basically has the fav item indicated as true.
Ex:
baseFruitList = {[FT1,apple],[FT2,grapes],[FT3,banana],[FT4,mango],[FT5,pears]}
favList = {[FT2,grapes],[FT4,mango]}
The final index list should have
finalIndexed = {false,true,false,true,false}
How can we achieve in Kotlin, without iterating through each element.
You can do
val finalIndexed = baseFruitList.map { it in favList }
assuming, like #Tenfour04 is asking, that name is guaranteed to be a specific value (including matching case) for a specific code (since that combination is how a data class matches another, e.g. for checking if it's in another list)
If you can't guarantee that, this is safer:
val finalIndexed = baseFruitList.map { fruit ->
favList.any { fav.code == fruit.code }
}
but here you have to iterate over all the favs (at least until you find a match) looking to see if one has the code.
But really, if code is the unique identifier here, why not just store those in your favList?
favList = listOf("FT2", "FT4") // or a Set would be more efficient, and more correct!
val finalIndexed = baseFruitList.map { it.code in favList }
I don't know what you mean about "without iterating through each element" - if you mean without an explicit indexed for loop, then you can use these simple functions like I have here. But there's always some amount of iteration involved. Sets are always an option to help you minimise that

creating a random list of coins from a repository

I'm trying to build a simple app that would display a coin collection for each user I type into a textfield. The coin collection would have to be unique to each user. I already have the repository for the coins. How do I generate a new random coin collection for each user? Each collection could have multiple coins of the same value but with different years.
object CoinRepository {
fun getCoinCollection(): List<Coin> {
return listOf(
Coin(
id = 1,
name = "Penny",
year = (1900..2022).random()
),
Coin(
id = 2,
name = "Nickel",
year = (1900..2022).random()
),
Coin(
id = 3,
name = "Dime",
year = (1900..2022).random()
),
Coin(
id = 4,
name = "Quarter",
year = (1900..2022).random()
),
Coin(
id = 5,
name = "Dollar",
year = (1900..2022).random()
)
)
}
}
data class Coin(
val id: Int,
val name: String,
val year: Int
)
You could do something like this:
import kotlin.random.Random
// Define your specific data in an enum, with all the relevant properties
enum class Denomination(val id: Int, val label: String) {
PENNY(1, "Penny"),
NICKEL(2, "Nickel"),
DIME(3, "Dime"),
QUARTER(4, "Quarter"),
DOLLAR(5, "Dollar");
companion object {
// a simple way to return one of the instances at random - the property
// avoids creating a new values() array every time it's called
val values = values()
fun random() = values.random()
}
}
// a basic way to keep the random date logic in the Coin class itself, using
// a default parameter. No validation involved obviously!
data class Coin(val id: Int, val label: String, val year: Int = (1900..2022).random())
// get a random number of Coins, within a certain min/max
fun getCoinCollection() = List(Random.nextInt(1, 10)) {
// pulls a random coin type and creates a Coin, letting its constructor
// handle the random date (you could do it here if you want)
Denomination.random().run { Coin(id, label) }
}
There's more than one way to organise it, I've thrown a few things in there so you can get some ideas of how you might do it. But it's basically a function that creates a list of random length (within limits), and then creates a Coin for each item, using a random Denomination
The Denomination enum is just a way to define your data, a fixed set of possible items with certain properties. Because enums generate that values() array automatically (containing all its instances) you can easily pick one at random. You could also extend the properties here to include a valid date range for each coin type, etc
You could just automatically generate the label and id values from the enum's name and ordinal properties (e.g. "PENNY" and 0) so you don't need to declare them explicitly - I feel like it's usually a good idea to decouple the data from how it's represented in the enum in code, but that's your call - I've included it so you can see how

Sum Mutable Map values inside a list of objects kotlin

I have a data class in Kotlin like this:
data class Activity(
var id: String? = "",
var prize: MutableMap<String?, Int?>? = null
)
And a list of this object:
var myList = listOf(Activity("A", prize={day_5=70, day_4=70}),
Activity("B", prize={day_5=40, day_4=80}))
The desired result is:
Activity("A", prize={total=140}),
Activity("B", prize={total=120})
So basically I want to sum the values of the prize map inside of each object.
I think that has something to do with transformation but I'm new to Kotlin and I couldn't find any resources over the internet, or maybe they were to complicated.
This algorithm is easy, just iterating through all the maps and replace elements with the sum:
val myList = listOf(
Activity("A", hashMapOf("day_5" to 70, "day_4" to 70)),
Activity("B", hashMapOf("day_5" to 40, "day_4" to 80)),
)
for (i in myList) {
val total = i.prize.values.sum()
i.prize.clear()
i.prize["total"] = total
}