How to convert set/println to string? - kotlin

I'm trying to add set to email as text
//Data class
#Entity(tableName = "inventory")
data class InventoryDataClass(
#PrimaryKey(autoGenerate = true)
var id: Int,
#NonNull var itemNumber: String,
#NonNull val itemDescription: String,
val currentInventory: Int?,
#NonNull val optimalInventory: Int,
#NonNull val minInventory: Int
)
// getting list from room
val inventoryList by mainViewModel.getInventoryItems.collectAsState(initial = emptyList())
//converting list to set
val emailNumber = inventoryList.map { item ->
println("Item Number: ${item.itemNumber} | Item Description: ${item.itemDescription} | Current Inventory: ${item.currentInventory.toString()}")}.toSet()
I'm able to get the output I need:
I/System.out: Item Number: 123| Item Description: item1 | Current Inventory: 47
I/System.out: Item Number: 456| Item Description: item2 | Current Inventory: 8
...
How can I get it as a string and add it to my email as text? So far i'm only able to get Kotlin.Unit
Log.d(TAG, "InventoryMainScreen: $emailNumber")
//output
D/MainActivity: InventoryMainScreen: [kotlin.Unit]

You can use joinToString:
val logsString = inventoryList.joinToString(separator = "\n") { item ->
"Item Number: ${item.itemNumber} | Item Description: ${item.itemDescription} | Current Inventory: ${item.currentInventory.toString()}"
}

You can append to a StringBuilder:
//Output this:
//Item Number: 123| Item Description: item1 | Current Inventory: 47
//Item Number: 123| Item Description: item1 | Current Inventory: 47
val emailNumber = StringBuilder()
inventoryList.forEach { item ->
emailNumber.append("Item Number: ${item.itemNumber} | Item Description: ${item.itemDescription} | Current Inventory: ${item.currentInventory}").append("\n")
}
emailNumber.removeSuffix("\n")

Related

How can I avoid a "Primary constructor call expected" in this case?

I've created a Person class and a Student class here in Kotlin:
In line 27, I'm trying to achieve a case where a user can create a "Student" class by providing 4 parameters: FirstName, LastName, Age, and Degree.
I've also written the equivalent code in Java. I'm trying to achieve the Java equivalent code's Secondary Constructor in line 30:
How can I avoid a "Primary constructor call expected" in the Kotlin code?
For your use case you don't even need secondary constructors. You could have optional arguments in the constructor. Like this for example:
open class Person(var firstName: String, var lastName: String, var age: Int? = null) {
override fun toString() = "$firstName | $lastName | $age"
}
class Student(firstName: String, lastName: String, var degree: String, age: Int? = null) : Person(firstName, lastName, age) {
override fun toString() = "$firstName | $lastName | $age | $degree"
}
To demonstrate:
fun main() {
val a = Person("Aaa", "aaA")
val b = Person("Bbb", "bbB", 20)
val c = Student("Ccc", "ccC", "degreeC")
val d = Student("Ddd", "ddD", "degreeD", 21)
println(a)
println(b)
println(c)
println(d)
}
Output:
Aaa | aaA | null
Bbb | bbB | 20
Ccc | ccC | null | degreeC
Ddd | ddD | 21 | degreeD

I want to merge two lists of Mcqs and True false type questions in List of quiz type

The data class of Mcqs look like this:
data class Mcqss(
var answer: String,
val mcqs: String,
val option1: String,
val option2: String,
val option3: String,
val option4: String,
var topicId: String,
var sequence: String,
)
True false data class:
data class tf(
val answer: String,
val question: String,
val topicId: String,
val sequence: String,
)
Quiz data class:
data class quiz(
var topicId: String,
var sequence: String,
var mcq_question:String,
var trf_question:String
)
Function to combine two lists:
fun <T, U> combine(first: ArrayList<Mcqss>, second: ArrayList<tf>): MutableList<Any> {
val list: MutableList<Any> = first.map { i -> i }.toMutableList()
list.addAll(second.map { i -> i })
return list
}
But when I execute this line it gives me a class cast exception:
val joined: ArrayList<quiz> = combine<Any,Any>(mcqlist, tfs) as ArrayList<quiz>
for (item in joined) {
item.sequence
}
Any suggestions please.
Please try next code:
fun combine(first: ArrayList<Mcqss>, second: ArrayList<Tf>): ArrayList<Quiz> {
// I assume the sizes of `first` and `second` lists are the same
require(first.size == second.size)
val result = mutableListOf<Quiz>()
for (i in 0 until first.size) {
val quiz = Quiz(first[i].topicId, first[i].sequence, ...)
result.add(quiz)
}
return ArrayList(result)
}
val joined: ArrayList<Quiz> = combine(mcqlist, tfs)
I would recommend to name classes starting with a capital letter, e.g. quiz->Quiz.
val mcqlist: List<Mcqss> = ...
val tfs: List<TrueFalse> = ...
val joined = mcqlist + tfs
for (item in joined) {
if (item is Mcqss) {
println(item.option1)
} else if (item is TrueFalse) {
println(item.question)
}
}

Kotlin read from room database in fragment

I write an application with room database and I have a problem with reading data and use it in the mine thread.
consider tables as those:
Table1
| ID | Name |
| -- | ------ |
| 1 | first |
| 2 | second |
Table2
| ID | Name | description | table1_id|
|--- | ---- | ------------- |----------|
| 1 | test | not important | 2 |
| 2 | test | not important | 1 |
I have no problem to display information about object from table2 since it is passed to the fragment, but when I try to get Name by ID from table1 I am unable to do so. I searched for information, but nothing I've found helped me.
My code:
private var _binding: FragmentXUpdateBinding? = null
private val binding get() = _binding!!
private lateinit var mXViewModel: XViewModel
private lateinit var mXTypeViewModel: XTypeViewModel
private val args by navArgs<XUpdateFragmentArgs>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentXUpdateBinding.inflate(inflater, container, false)
mXViewModel = ViewModelProvider(this)[XViewModel::class.java]
mXTypeViewModel = ViewModelProvider(this)[XTypeViewModel::class.java]
setHasOptionsMenu(true)
val XType = getXTypeName(args.currentX.X_type_id)
binding.XTypeText.text = XType
binding.etXName.setText(args.currentX.name)
binding.etXDescription.setText(args.currentX.description)
binding.buttonUpdateX.setOnClickListener {
updateItem()
findNavController().navigate(my.food.R.id.action_XUpdateFragment_to_XListFragment)
}
return binding.root
}
private fun getXTypeName(id: Int): String {
var XTypeString = ""
mXTypeViewModel.readAllData.observe(viewLifecycleOwner) { type ->
type?.forEach {
if (it.id == id) {
XTypeString = it.name
}
}
}
return XTypeString //it returns "" since database thread is still ongoing
}
Please note the comment in the second from the end line.
What's the solution to my headache?
THANKS!!!!
The problem is that you are trying to retrieve XTypeString before your asynchronous task(readAllData from ViewModel) has completed. You should move the binding of XTypeText inside observe, as below. Note the change in function signature for bindXTypeText().
private fun bindXTypeText(id: Int) {
var XTypeString = ""
mXTypeViewModel.readAllData.observe(viewLifecycleOwner) { type ->
type?.forEach {
if (it.id == id) {
XTypeString = it.name
}
}
binding.XTypeText.text = XTypeString //moved the assignment of XtypeText here.
}
}

How to combine fields of objects by a certain attribute?

I need to filter these Person objects by name, then count the number of Devices for each unique name.
data class Device(val name: String, val price: Int)
data class Person(val name: String, val age: Int, val devices: List<Device>)
data class PersonFinal(val name: String, val age: Int, val count: Int)
val person1 = Person("Jack", 20, listOf(Device("Samsung", 1500), Device("iPhone", 2500)))
val person2 = Person("Jack", 20, listOf(Device("Samsung", 3500), Device("iPhone", 5500)))
val person3 = Person("John", 20, emptyList())
The final result should be:
// [PersonFinal(name=Jack, age=20, count=4), PersonFinal(name=John, age=20, count=0)]
How can I do this using Kotlin?
If someone needed, my final solution looks like:
listOf(person1, person2, person3)
.groupBy { it.name }
.map { (_, list) ->
PersonFinal(list.first().name, list.first().age, list.sumBy { it.devices.count() })
}
Prints [PersonFinal(name=Jack, age=20, count=4), PersonFinal(name=John, age=20, count=0)]

Kotlin - Random.nextInt Range

Aim of code: Shopping system,function which shows a matched product name from the warehouse
what is the no. range of Random.nextInt() if no no. is assigned inside ()?
in fun fillWarehouse, if i do not set no. inside "StockUnit(Random.nextInt(),Random.nextInt())", when i call println("Number of items: ${p.availableItems}") in main, No. -890373473 / 1775292982 etc. were generated.
if i set 100 inside like "StockUnit(Random.nextInt(100),Random.nextInt(100))", No. 263 / 199 etc. were generated. why is it not within 0-100? may i know how to change my code, so that "Number of items" is within 100?
any links or topics should i work for, to write better code?
i cannot find the answers from https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.random/
Sincere thanks!
fun main(args: Array<String>) {
val warehouse = Warehouse()
...
println("Show info")
showInfo(warehouse)
}
fun showInfo(warehouse: Warehouse) {
println("Get Info")
val input = readLine() ?: "-"
val p = warehouse.getProductByName(input)
if (p != null) {
println("Product: $p")
println("Number of items: ${p.availableItems}")
println("Profit: ${p.profitPerItem}")
}
}
class Warehouse {
private val products = mutableListOf<Product>()
...
fun getProductByName (productName: String): Product? {
for (prod in products)
if (prod.productName == productName) return prod
return null
}
fun fillWarehouse (productName: String,
basePrice: Double,
productDescription: String,
chargeOnTop: Double = 50.0,
intialStockUnits: Int = 3) {
val newProduct = Product(productName, basePrice, basePrice * (1 + chargeOnTop / 100), productDescription)
//add quantity, daysBeforeExpiration
for (i in 1 .. intialStockUnits){
val unit = StockUnit(Random.nextInt(),Random.nextInt() )
newProduct.addStock(unit)
}
open class Product(
val productName: String,
var basePrice: Double,
open val salesPrice: Double,
val description: String) {
...
var stockUnits = mutableListOf<StockUnit>()
...
// availableItems = Total of stockUnits
var availableItems: Int = 0
get() = stockUnits.sumBy { it.quantity }
}
class StockUnit(var quantity:Int, var daysBeforeExpiration:Int){
...
}