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

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

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.

Kotlin / What i need to pass to init: (index: Int)->Contact?

I have a question, what value I need to pass to "init: (index: Int)->Contact" as it expects:
The integer literal does not conform to the expected type (Int) ->
TypeVariable(T)
Snippet of the function
#Composable
fun ContactList(textVal: MutableState<TextFieldValue>, navigateToProfile: (Contact) -> Unit) {
var contacts = remember { DataProvider.contactList }
var contactList = contacts.toMutableList()
var filteredContacts: MutableList<Contact>
LazyColumn(
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
) {
val searchText = textVal.value.text
filteredContacts = if (searchText.isEmpty()){
contactList
}
else{
val resultList = MutableList<Contact>(10, "init: (inded: Int)->Contact")
for (contact in contactList) {
if (contact.contains(searchText.lowercase())) {
resultList.add(contact)
}
}
resultList
}
items(filteredContacts) {
ContactListItem(contact = it, navigateToProfile)
}
}
}
Snippet of the Contact
data class Contact(
val id: Int,
val name: String,
val description: String,
val recipe: String,
val ImageId: Int = 0
)
(index: Int)->Contact means: a function that receives Int and returns Contact. But I guess this does not really solve your problem.
The problem is that you use MutableList "constructor" (it is actually a function) which is intended to create a list with exactly 10 items. Then you need to provide these items and this is what init function is for. What you actually need here is to create an empty list and you can do this with:
val resultList = mutableListOf<Contact>()
However, if you just need to filter some collection and create another one, it is much easier to use filter():
val resultList = contactList.filter { it.contains(searchText.lowercase()) }
If Contact.contains() is an operator then we can also simplify it to:
val resultList = contactList.filter { searchText.lowercase() in it }

How to find last node that satisfies where predicate in singly linked list?

write a method "lastWhere" that accepts a function called "where" of type (T) -> Boolean. The method returns the last element of type T to which the "where" function applies. If no matching element is found, null is returned.
call the method "lastwhere" on the linked list below. Find the last game that is more than 10 euros.
So far I've got this Code going for me.
I assume the only important piece of Code I need to edit is the "fun lastWhere" for task number 1)
the second task wants me to implement a way on the main function to find the last Game that is cheaper than 10 Euros.
class LinkedList<T> {
data class Node<T>(val data: T, var next: Node<T>?)
private var first: Node<T>? = null
override fun toString(): String = first?.toString() ?: "-"
fun isEmpty() = first == null
fun addLast(data: T) {
if (first == null) {
first = Node(data, first)
return
}
var runPointer = first
while (runPointer?.next != null) {
runPointer = runPointer.next
}
runPointer?.next = Node(data, null)
}
fun lastWhere (where: (T) -> Boolean): T? { // "where" function needs to be implemented
if (isEmpty()) return null
else {
var runPointer = first
while (runPointer?.next != null ) {
runPointer = runPointer.next
}
return runPointer?.data
}
}
}
data class Game(val title: String, val price: Double)
fun main() {
val list = LinkedList<Game>()
list.addLast(Game("Minecraft", 9.99))
list.addLast(Game("Overwatch", 29.99))
list.addLast(Game("Mario Kart", 59.99))
list.addLast(Game("World of Warcraft", 19.99))
var test = list.lastWhere ({it.price >= 10.00}) // This is probably wrong too, since I haven't got task 1) working
println (test)
}
Would appreciate any help!
Since you only store a reference to first node, you don't have any choice but to start at first and iterate. you will also have to keep a reference to last item that satisfied the where predicate, and keep updating this reference with every iteration.
fun lastWhere (where: (T) -> Boolean): T? {
var runPointer = first
var item: T? = null // init item to null, if nothing is found we return null
while (runPointer != null ) {
// For every node, execute the where function and if it returns true
// then update the return value
if(where(runPointer.data)) { item = runPointer.data }
runPointer = runPointer.next
}
return item
}

Why 'add' method doesn't work for mutableListOf()?

I have my own converter from Strings to List
object TypeConverter {
fun stringToListLong(text: String): List<Long> {
val listLong = mutableListOf<Long>()
val listString = text.split(",").map { it.trim() }
listString.forEach {
listLong.add(it.toLong())
}
return listLong
}
}
Then when I try to use it like below it shows the error(Unresolved reference: add)
val someString = "something"
var ids = TypeConverter.stringToListLong(someString)
ids.add(some long value)
Why?
You're returning a List<>, so ids is a List<>, therefore it does not have mutation operations. Make stringToListLong return MutableList<Long>.

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

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