Kotlin How to use java streams .map() in kotlin to map a different object response - kotlin

I am trying to map an array of objects to another array with different kind of objects, I used to do this using streams in java 8 it was pretty straight forward, instantiate an object set its values and return the object. I just switched to Kotlin and really sometimes is more confusing to do this kind of operations. All the examples I found are really simple and could not find something I want.
I have this BalanceMap class:
data class BalanceMap #JsonCreator constructor(
var balType: String,
var value: Any
)
I am getting the data from web service.
val balances: List<AcctBal> = res.getAcctBals();
the AcctBal class looks like this
public class AcctBal {
#SerializedName("CurAmt")
#Expose
private CurAmt curAmt;
#SerializedName("Desc")
#Expose
private String desc;
#SerializedName("ExpDt")
#Expose
private LocalDateTime expDt;
}
and try to map that response to var balanceList: List<BalanceMap>
balances.map {}
--> var balanceList: List<BalanceMap> = balances.map { t -> fun AcctBal.toBalanceMap() = BalanceMap(
balType = "",
value = ""
)}
I want to do something like this:
List<ProductDetail> details = acctBal.stream().filter(f -> f.getBalType() != null).map(e -> {
String bal = e.getBalType();
if (avalProductInfo.getBankId().equals("00010016")) {
bal = e.getBalType();
}
ProductDetail detail = new ProductDetail();
detail.setItem(bal);
if (e.getCurAmt() != null) {
detail.setValue(e.getCurAmt().getAmt().toString());
} else if (e.getRelationDt() != null) {
detail.setValue(e.getRelationDt().toGregorianCalendar().getTimeInMillis());
} else if (e.getMemo() != null) {
detail.setValue(e.getMemo());
}
return detail;
}).collect(toList());
I've been experimenting but is always wrong, any help will be highly appreciated. Happy coding!

some quick prototyping
details = acctBal
.filter{ f -> f.getBalType() != null }
.map { it -> mapToProductDetail (it) }
you can have a look here

Thanks to #Hakob Hakobyan for pointing in the right direction,
I left my solution like this:
fun mapRs(rs: AthProductResponse): BalanceByAccountRs {
val res = rs.getPartyAcctRelRec();
val balances: List<AcctBal> = res.getAcctBals();
val account = Account(res.getPartyAcctRelInfo().depAcctId.acctId, res.getPartyAcctRelInfo().depAcctId.acctType)
var balanceList: List<BalanceMap> = balances
.filter { f -> f.getDesc() != null }
.map { it -> mapToProductDetail(it) }
.toList()
return BalanceByAccountRs(account, balanceList)
}
fun mapToProductDetail(bal: AcctBal): BalanceMap {
var propertyValue: Long = 0L;
if(bal.getExpDt() != null) {
propertyValue = Timestamp.valueOf(bal.getExpDt()).getTime()
} else {
propertyValue = bal.getCurAmt().getAmt().toLong()
}
return BalanceMap(bal.getDesc(), propertyValue)
}
Just in case someone is going through the same. Happy coding

Related

Returning one of different object types from single function in kotlin

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.

find value in arraylist in kotlin

Hey I am working in kotlin. I am working on tree data structure. I added the value in list and now I want to find that value and modified their property. But I am getting the error.
VariantNode, StrengthNode, ProductVariant
StrengthNode.kt
class StrengthNode : VariantNode() {
var pricePerUnit: String? = null
var defaultValue = AtomicBoolean(false)
}
ActivityViewModel.kt
class ActivityViewModel : ViewModel() {
var baseNode: VariantNode = VariantNode()
private val defaultValueId = "12643423243324"
init {
createGraph()
}
private fun createGraph() {
val tempHashMap: MutableMap<String, VariantNode> = mutableMapOf()
val sortedList = getSortedList()
sortedList.forEach { productVariant ->
productVariant.strength?.let { strength ->
if (tempHashMap.containsKey("strength_${strength.value}")) {
baseNode.children.contains(VariantNode(strength.value)) // getting error
return#let
}
val tempNode = StrengthNode().apply {
value = strength
pricePerUnit = productVariant.pricePerUnit?.value
if (productVariant.id == defaultValueId) {
defaultValue.compareAndSet(false, true)
}
}
baseNode.children.add(tempNode)
tempHashMap["strength_${strength.value}"] = tempNode
}
productVariant.quantity?.let { quantity ->
if (tempHashMap.containsKey("strength_${productVariant.strength?.value}_quantity_${quantity.value}")) {
return#let
}
val tempNode = QuantityNode().apply {
value = quantity
}
val parent =
tempHashMap["strength_${productVariant.strength?.value}"] ?: baseNode
parent.children.add(tempNode)
tempHashMap["strength_${productVariant.strength?.value}_quantity_${quantity.value}"] =
tempNode
}
productVariant.subscription?.let { subscription ->
val tempNode = SubscriptionNode().apply {
value = subscription
}
val parent =
tempHashMap["strength_${productVariant.strength?.value}_quantity_${productVariant.quantity?.value}"]
?: baseNode
parent.children.add(tempNode)
}
}
baseNode
}
}
I am getting error on this.
I want to find that node value and modified other property.
Your class VariantNode only has a single no-arg constructor, but you're trying to call it with arguments, hence the error
Too many arguments for public constructor VariantNode() defined in com.example.optionsview.VariantNode
Either you have to provide a constructor, that matches your call, e.g.
open class VariantNode(var value: ProductValue?) {
var children: MutableList<VariantNode> = arrayListOf()
}
or you need to adjust your code to use the no-arg constructor instead.
val node = VariantNode()
node.value = strength.value
baseNode.children.contains(node)
Note however, that your call to contains most likely will not work, because you do not provide a custom implementation for equals. This is provided by default, when using a data class.
If you just want to validate whether baseNode.children has any element, where value has the expected value, you can use any instead, e.g.:
baseNode.children.any { it.value == strength.value }

How to Store Class in a Variable Kotlin

i want to store a Class in a variable, the purpose is to check if other variable is an instanceOf the class
Here is My Code :
when (it.itemId) {
R.id.vend_list -> {
replace(R.id.fragmentContainer, vendingList)
id = VendListClass
}
R.id.label -> {
replace(R.id.fragmentContainer, label)
id = LabelClass
}
R.id.home -> {
replace(R.id.fragmentContainer, mainMenu)
id = MainMenuClass
}
R.id.statistic -> {
replace(R.id.fragmentContainer, statistic)
id = StatisticClass
}
else -> {}
}
for(fragment in supportFragmentManager.fragments){
if(fragment !is id){
remove(fragment)
}
}
I don't know your exact requirement. But it probably could be designed in other ways, enum? sealed classes? inheritances?
Anyway, straight to your question, hope this helps:
val listCls = List::class
val strCls = String::class
val listObj = listOf(1,2,3)
println("check listObj and listCls: ${listCls.isInstance(listObj)}")
println("check listObj and strCls: ${strCls.isInstance(listObj)}")
output:
check listObj and listCls: true
check listObj and strCls: false
You can store a class reference in a KClass<*> variable using ::class or in a Class<*> variable using ::class.java
So based on your original code, this is how you could do it
// this has to be a nullable type because of your else option
var id: KClass<*>? = null
when (it.itemId) {
R.id.vend_list -> {
replace(R.id.fragmentContainer, vendingList)
id = VendListClass::class
}
R.id.label -> {
replace(R.id.fragmentContainer, label)
id = LabelClass::class
}
R.id.home -> {
replace(R.id.fragmentContainer, mainMenu)
id = MainMenuClass::class
}
R.id.statistic -> {
replace(R.id.fragmentContainer, statistic)
id = StatisticClass::class
}
else -> {}
}
for(fragment in supportFragmentManager.fragments){
// if the else option from when has to remove all
// fragments, just flip the condition to
// if(id == null || id.isInstance(fragment))
if(id != null && id.isInstance(fragment)){
remove(fragment)
}
}
Thanks #Kent for helping, but I figured out how to solve this in an ineffective way.
Here is my code:
val obj1 = Object1()
val obj2 = Object2()
val obj3 = Object3()
val obj4 = Object4()
var id : SuperObject? = null
when(certainConditions) {
option1 -> id = Object1()
option2 -> id = Object2()
option3 -> id = Object3()
option4 -> id = Object4()
}
val otherObject = Object1()
if(id == otherObject) {
//process logic
}
I'm still looking for the more effective way though.
If anyone found out a better way, please share your answer.

Kotlin combined usage of arrow, lamda, ?, . , and let. what it mean?

fun theItemDTO.toDomainModel(
domainOrderId: String,
pIds: List<Long> = emptyList()
): theItem = let { dto ->
OrderProtoBuilders.theItem {
this.id = dto.id.toString()
skuId = dto.catalogEntryId.toString()
orderId = domainOrderId
quantity = dto.quantity
unitPrice = dto.unitPrice
totalPrice = dto.totalPrice
price = null
for (pId in pIds)
addpId(pId.toString())
dto.someMap[MAP_A]
?.let(::setAId)
dto.someMap[MAP_B]
?.let(::setBId)
}
}
For above kotlin function, my order of questions starts from the inner block code to the outter block:
1. what is dto.someMap[MAP_A]
?.let(::setAId) doing? It looks like a reassignment of someMap[MAP_A], but also a declaration of let something... what is it doing?
the meaning of so many { wrapper is the same as return. correct? Since I did read -> is like a function returning value automatically. So the end value of ...: theItem is really what OrderProtoBuilders.theItem {... created?
let { dto ->
OrderProtoBuilders.theItem {...
dto.someMap[MAP_A]?.let(::setAId) can be expanded like
val value = dto.someMap[MAP_A]
if (value != null) {
setAId(value)
}
2.
Yep, it's correct. You can think about it like
//the whole function will return item created by OrderProtoBuilders.theItem
fun theItemDTO.toDomainModel(
domainOrderId: String,
pIds: List<Long> = emptyList()
): theItem = let { dto ->
//return OrderProtoBuilders.theItem
OrderProtoBuilders.theItem {
//do some additional initialization of the Item
}
}

Kotlin general setter function

I am new to kotlin. I wonder if this is possible
I wish to create a function that will change the value of the properties of the object and return the object itself. The main benefit is that I can chain this setter.
class Person {
var name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
try {
// the line below caused error
this[propName] = value
} catch(e:Exception){
println(e.printStackTrace())
}
}
}
}
//usage
var person = Person(null,null)
person
.setter(name, "Baby")
.setter(age, 20)
But I get error "unknown references"
This question is marked as duplicate, however the possible duplicate question specifically want to change the property of "name", but I wish to change anyProperty that is pass from the function to object. Can't seem to connect the dot between two questions. #Moira Kindly provide answer that explain it. thankyou
Why not just simplify your answer to
fun setter(propName: String, value: Any): Person {
val property = this::class.memberProperties.find { it.name == propName }
when (property) {
is KMutableProperty<*> ->
property.setter.call(this, value)
null ->
// no such property
else ->
// immutable property
}
}
Java reflection isn't needed, its only effect is to stop non-trivial properties from being supported.
Also, if you call it operator fun set instead of fun setter, the
this[propName] = value
syntax can be used to call it.
After googling around, I think I can provide an answer, but relying on java instead of kotlin purely. It will be great if someone can provide a better answer in kotlin.
class Person(
var name: String,
val age: Int
){
fun setter(propName: String, value: Any): Person{
var isFieldExistAndNotFinal = false
try{
val field = this.javaClass.getDeclaredField(propName)
val isFieldFinal = (field.getModifiers() and java.lang.reflect.Modifier.FINAL == java.lang.reflect.Modifier.FINAL)
if(!isFieldFinal) {
// not final
isFieldExistAndNotFinal = true
}
// final variable cannot be changed
else throw ( Exception("field '$propName' is constant, in ${this.toString()}"))
} catch (e: Exception) {
// object does not have property
println("$e in ${this.toString()}")
}
if(isFieldExistAndNotFinal){
val property = this::class.memberProperties.find { it.name == propName }
if (property is KMutableProperty<*>) {
property.setter.call(this, value)
}
}
return this;
}
}
usage like this
person
.setter(propName = "age", value = 30.00)
.setter(propName = "asdf", value = "asdf")
.setter(propName = "name", value = "A Vidy")
You have error because when you do this[propName] = value you are trying to use this as a list, but it is not a list, it is a Person and it doesn't overload the [] operator.
What you can do is to add a check for the property that is setted:
class Person {
privavar name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
if (propName == "name" && value is String?) {
it.name = value as String?
} else if (propName == "age" && value is Int?) {
it.age = value as Int?
} else {
// handle unknown property or value has incorrect type
}
}
}
}
Another more dynamic solution without reflection:
class Person {
private var fields: Map<String, Any?> = HashMap()
fun setter(propName:String, value:Any): Person{
return this.apply {
it.fields[propName] = value;
}
}
fun getName() = fields["name"]
}
If you want to get rid of the getters as well then you need to use reflection.