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){
...
}
Related
I have the following structure at present:
#Entity
#Table(name = "table_app_settings")
data class AppSetting(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "app_setting_id")
val id: Long? = null,
#Column(name = "app_setting_name")
val name: String = "",
#Column(name = "app_setting_value")
var value: String = "",
#Column(name = "app_setting_type")
val type: AppSettingType,
)
enum class AppSettingType {
CHAR,
STRING,
BYTE,
SHORT,
INT,
LONG,
DOUBLE,
FLOAT,
BOOLEAN,
}
This is then saved to the database with the following:
override fun saveAppSetting(setting: AppSetting): DatabaseResult<AppSetting> {
log.info("Saving App Setting ${setting.name} to database.")
return try {
// Attempt to save the entity to the database. If we do not throw an exception, return success.
val savedSetting = appSettingsRepository.save(setting)
DatabaseResult(
code = ResultCode.CREATION_SUCCESS,
entity = savedSetting
)
} catch(exception: DataAccessException) {
log.error("Unable to save App Setting ${setting.name} to database. Reason: ${exception.message}")
DatabaseResult(
code = ResultCode.CREATION_FAILURE
)
}
}
Now, let's say that I wish to save a Char type to database, I figure I would use the following:
override fun saveAppSetting(name: String, value: Char): DatabaseResult<Char> {
val appSettingResult = saveAppSetting(AppSetting(
name = name,
value = value.toString(),
type = AppSettingType.CHAR,
))
return if(appSettingResult.code != ResultCode.CREATION_FAILURE) {
val entity = getAppSetting<Char>(appSettingResult.entity?.name!!).entity.toString().first()
DatabaseResult(
code = appSettingResult.code,
entity = entity
)
} else {
DatabaseResult(
code = ResultCode.CREATION_FAILURE,
)
}
}
I also figured that I would need to do the following in order to retrieve the correct object type:
override fun getAppSetting(name: String): DatabaseResult<Any?> {
log.info("Getting App Setting $name from database.")
val appSetting = appSettingsRepository.findAppSettingByName(name)
return if(appSetting != null) {
log.info("App Setting $name has ID of ${appSetting.id} within the database")
when(appSetting.type) {
AppSettingType.CHAR -> {
DatabaseResult<Char>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.first(),
)
}
AppSettingType.STRING -> {
DatabaseResult<String>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value,
)
}
AppSettingType.BYTE -> {
DatabaseResult<Byte>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toByte(),
)
}
AppSettingType.SHORT -> {
DatabaseResult<Short>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toShort(),
)
}
AppSettingType.INT -> {
DatabaseResult<Int>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toInt(),
)
}
AppSettingType.LONG -> {
DatabaseResult<Long>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toLong(),
)
}
AppSettingType.DOUBLE -> {
DatabaseResult<Double>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toDouble(),
)
}
AppSettingType.FLOAT -> {
DatabaseResult<Float>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toFloat()
)
}
AppSettingType.BOOLEAN -> {
DatabaseResult<Boolean>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toBoolean()
)
}
}
} else {
log.error("App Setting $name does not seem to exist within the database.")
DatabaseResult(
code = ResultCode.FETCH_FAILURE
)
}
However, when I then wish to use said object, I still have to write something like the following:
val newBarcode = getAppSetting("barcode_value").entity.toString().toInt()
Assuming I've "initialised" barcode_value with a value of 177 (for example).
How can I get the function to return what I need without having to do .toString.to...()?
Yes this all possible, here is a simplified demo, firstly
import kotlin.reflect.KClass
data class AppSetting(
val id: Long? = null,
val name: String = "",
var value: String = "",
val type: AppSettingType,
)
enum class AppSettingType(val clazz: KClass<out Any>) {
CHAR(Char::class),
STRING(String::class),
INT(Int::class),
}
So I added a clazz so from the enum we know the Kotlin type
and now a function to simulate your repository fetch
fun findAppSettingByName(name: String): AppSetting? {
return when(name) {
"Char thing" -> AppSetting(value= "C", type = AppSettingType.CHAR)
"String thing" -> AppSetting(value= "Str", type = AppSettingType.STRING)
"Int thing" -> AppSetting(value= "42", type = AppSettingType.INT)
else -> throw IllegalArgumentException()
}
}
Next in the function declaration I have made it generic with T and for the purposes of the demo removed the DatabaseResult container. Then I added a clazz parameter which is the typical Java way of carrying the required class information into the function:
fun <T : Any> getAppSetting(name: String, clazz: KClass<T>): T? {
val appSetting: AppSetting? = findAppSettingByName(name)
return appSetting?.let {
require(clazz == appSetting.type.clazz) {
"appSetting.type=${appSetting.type.clazz} mismatched with requested class=${clazz}"
}
when (appSetting.type) {
AppSettingType.CHAR -> appSetting.value.first()
AppSettingType.STRING -> appSetting.value
AppSettingType.INT -> appSetting.value.toInt()
} as T
}
}
the as T is important to cast the values into the required return type - this is unchecked but the when() clause should be creating the correct types.
Now let's test it:
val c1: Char? = getAppSetting("Char thing", Char::class)
val s1: String? = getAppSetting("String thing", String::class)
val i1: Int? = getAppSetting("Int thing", Int::class)
println("c1=$c1 s1=$s1 i1=$i1")
val c2: Char? = getAppSetting("Char thing")
val s2: String? = getAppSetting("String thing")
val i2: Int? = getAppSetting("Int thing")
println("c2=$c2 s2=$s2 i2=$i2")
}
The output is
c1=C s1=Str i1=42
c2=C s2=Str i2=42
But how do c2/s2/i2 work, the final part is this function
inline fun <reified T : Any> getAppSetting(name: String) = getAppSetting(name, T::class)
This is reified generic parameters... there is no need to pass the clazz because this can be found from the data type of the receiving variable.
There are many articles about this advanced topic, e.g.
https://typealias.com/guides/getting-real-with-reified-type-parameters/
https://medium.com/kotlin-thursdays/introduction-to-kotlin-generics-reified-generic-parameters-7643f53ba513
Now, I didn't completely answer what you wanted because you wanted to receive a DatabaseResult<T> wrapper. What might be possible, is to have a function that returns DatabaseResult<T> and you can obtain the T from it as the "clazz" parameter, but I'll leave that for someone else to improve on :-) but I think that gets you pretty close.
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)
}
}
I want to sort a list based on their latitude and longitude...
Here is my code:
import java.util.*
import com.google.gson.GsonBuilder
import java.io.File
import java.io.InputStream
import java.util.Comparator
data class Property(val Pcode: Int, val Locality: String, val State: String, val Comments: String, val Category: String, val Longitude: Double, val Latitude: Double)
class SortPlaces(currentLatitude: Double, currentLongitude: Double) : Comparator<Property> {
var currentLat: Double
var currentLng: Double
override fun compare(property1: Property, property2: Property): Int {
val lat1: Double = property1.Latitude
val lon1: Double = property1.Longitude
val lat2: Double = property2.Latitude
val lon2: Double = property2.Longitude
val distanceToPlace1 = distance(currentLat, currentLng, lat1, lon1)
val distanceToPlace2 = distance(currentLat, currentLng, lat2, lon2)
return (distanceToPlace1 - distanceToPlace2).toInt()
}
fun distance(fromLat: Double, fromLon: Double, toLat: Double, toLon: Double): Double {
val radius = 6378137.0 // approximate Earth radius, *in meters*
val deltaLat = toLat - fromLat
val deltaLon = toLon - fromLon
val angle = 2 * Math.asin(
Math.sqrt(
Math.pow(Math.sin(deltaLat / 2), 2.0) +
Math.cos(fromLat) * Math.cos(toLat) *
Math.pow(Math.sin(deltaLon / 2), 2.0)
)
)
return radius * angle
}
init {
currentLat = currentLatitude
currentLng = currentLongitude
}
}
fun main(args: Array<String>) {
val command = Scanner(System.`in`)
val running = true
while (running) {
val inputStream: InputStream = File("./src/main/kotlin/suburbs.json").inputStream()
val inputString = inputStream.bufferedReader().use { it.readText() }
val gson = GsonBuilder().create()
val packagesArray = gson.fromJson(inputString , Array<Property>::class.java).toList()
println("Please enter a suburb name: ")
val suburbName = command.nextLine()
println("Please enter the postcode: ")
val postcode = command.nextLine()
val userProperty: Property? = packagesArray.find{ it.Locality.toLowerCase().equals(suburbName.toLowerCase()) && it.Pcode == postcode.toInt()}
//sort the list, give the Comparator the current location
Collections.sort(packagesArray, new SortPlaces(userProperty.Latitude, userProperty.Longitude));
}
command.close()
}
I got error: Too many arguments for public open fun <T : Comparable<T!>!> sort(list: (Mutable)List<T!>!): Unit defined in java.util.Collections
at my sort{} function
my userProperty has to be Property? because the find{} method return Property?
Then Collections.sort() can not sort Property? type because the SortPLaces only accept Comparator not Comparator<Property?>
What should I do?
There are multiple errors in your code. To create a new object in Kotlin, you don't write the word new like you do in Java. Also, as you have noticed, find returns a nullable type - Property?. You need to check for nulls when using userProperty. A Property matching the criteria you want may not necessarily be found, after all.
if (userProperty != null) {
Collections.sort(packagesArray, SortPlaces(userProperty.Latitude, userProperty.Longitude))
} else {
// no property is found! Think about what you should do in such a case
}
Since you are sorting the list in line, you should not make an immutable list with toList when you are deserialising the JSON, but rather a MutableList:
val packagesArray = gson.fromJson(inputString, Array<Property>::class.java).toMutableList()
Also, you seem to be using a lot of Java APIs. In Kotlin, a lot of the Java APIs that you are using have more idiomatic Kotlin counterparts. To sort the list, you don't need the SortPlaces class at all. Simply use sortBy on the array, and call your distance function in the lambda.
data class Property(
val pcode: Int,
val locality: String,
val state: String,
val comments: String,
val category: String,
val longitude: Double,
val latitude: Double,
)
fun distance(fromLat: Double, fromLon: Double, toLat: Double, toLon: Double): Double {
val radius = 6378137.0 // approximate Earth radius, *in meters*
val deltaLat = toLat - fromLat
val deltaLon = toLon - fromLon
val angle = 2 * asin(
sqrt(
sin(deltaLat / 2).pow(2.0) +
cos(fromLat) * cos(toLat) *
sin(deltaLon / 2).pow(2.0)
)
)
return radius * angle
}
fun main(args: Array<String>) {
val running = true
while (running) {
val inputStream = File("./src/main/kotlin/suburbs.json").inputStream()
val inputString = inputStream.bufferedReader().use { it.readText() }
val gson = GsonBuilder().create()
val packagesArray = gson.fromJson(inputString , Array<Property>::class.java).toMutableList()
println("Please enter a suburb name: ")
val suburbName = readLine()
println("Please enter the postcode: ")
val postcode = readLine()
val userProperty = packagesArray.find {
it.locality.lowercase() == suburbName?.lowercase() && it.pcode == postcode?.toInt()
}
//sort the list, give the Comparator the current location
if (userProperty != null) {
packagesArray.sortBy {
distance(userProperty.latitude, userProperty.longitude, it.latitude, it.longitude)
}
} else {
// did not find such a property!
}
}
}
I wanna call run the following function addToCart() in the main, but such error appears, may i know how should i change my code?
Thanks!
fun addToCart(){
println("Productname : ")
var nameInput= readLine() ?: "-"
println("Num of $nameInput ")
var num = readLine()!!.toInt()
var got : Product? = warehouse.getProductByName(nameInput)
shoppingCart.addToList(nameInput, num)
}
class ShoppingCart () {
private val productAndQuantityList = mutableListOf<Pair<Product, Int>>()
fun addToList(name: String, quantity: Int){
productAndQuantityList.add(Pair(name, quantity))
}
....
}
I think you need not null checks before calling add.
fun addProductToCart(shoppingCart: ShoppingCart, warehouse: Warehouse) {
println("Product name: ")
var productName_E = readLine() ?: "-"
println("No. of $productName_E ")
var num = readLine()?.toInt()
var got: Product? = warehouse.getProductByName(productName_E)
if (num != null && got != null) {
shoppingCart.productAndQuantityList.add(Pair(first = got, second = num))
}
}
Your ShoppingCart class should be something like the class below.
class ShoppingCart {
val productAndQuantityList = mutableListOf<kotlin.Pair<Product, Int>>()
...
}
I read carefully the ArrayList documentation in Kotlin and apparently there is no way to search a value in ArrayList starting from a pointer. The alternative is write your own function iterating the right elements in ArrayList and testing the condition.
So I've programmed the following code:
fun <T> ArrayList<T>.findNext(cond: (T) -> Boolean, p: Int = 0): Int {
for (i in p..this.lastIndex)
if (cond(this[i])) return i
return -1
}
data class Person (
var name: String,
var age: Int
)
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({it.name=="Paul"})) // 0
println(v.findNext({it.name=="Paul"}, 1)) // 2
println(v.findNext({it.name=="Paul"}, 3)) // -1
}
Is there something better than this?
You can avoid any intermediate collections:
inline fun <T> List<T>.findNext(p: Int = 0, cond: (T) -> Boolean) =
listIterator(p).withIndex().asSequence().find { cond(it.value) }?.let { it.index + p }
By swapping the arguments you can call it like this:
println(v.findNext {it.name=="Paul"}) // 0
println(v.findNext(1) {it.name=="Paul"}) // 2
println(v.findNext(3) {it.name=="Paul"}) // null
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({ it.name == "Paul" },0))//IndexedValue(index=0, value=Person(name=Paul, age=22))
println(v.findNext({ it.name == "Paul" },2))//IndexedValue(index=2, value=Person(name=Paul, age=50))
println(v.findNext({ it.name == "Paul" },3))//null
}
private fun <T> List<T>.findNext(cond: (T) -> Boolean, position: Int): IndexedValue<T>? {
return withIndex().filter { it.index >= position }.firstOrNull { cond(it.value) }
}
maybe use withIndex and a filter ?
val arrayNames = listOf<String>("Paul", "Ann", "Paul", "Roger","Peter")
arrayNames.withIndex().filter {
it.value == "Paul" //value contains the original name
}.forEach{
println(it.index) //indext contains the position.
}
this will give you the output 0 and 2
for your case (person object instead of String) you will use
it.value.name == "Paul"