Kotlin mapping a list that has a list inside - kotlin

I am trying to map a list of items, that has another list inside which should be mapped into the same type of items. however, i end up having List<List> and can not flatten.
TropicalFruit(
val someStuff: String
)
Fruits(
val type: String,
val tropicalFruits: List<TropicalFruit>
)
FruitUiModel(
...
)
val listOfFruits: List<Fruit>
listOfFruits.map { fruit ->
if (fruit.type == "tropical") {
tropicalFruits.map {
FruitUiModel(it.someStuff)
}
} else {
FruitUiModel(fruit.type)
}
}
As a result, I want to achieve a List instead of List
Any being both FruitUiModel and List
So the list ends up being something like listOf(fruitUiModel, fruitUiModel, listOfFruitUiModels)
and i need to flatten the listOfFruitUiModels

You have to use flatMap instead of map:
listOfFruits.flatMap { fruit ->
if (fruit.type == "tropical") {
fruit.tropicalFruits.map { FruitUiModel(it.someStuff) }
} else {
FruitUiModel(fruit.type)
}
}

Related

sort an array by providing field name dynamically

I have a data class with about 20 fields:
data class Entry {
entryType: string,
order: int
....
}
now an array is created with the class
val entries = listOf<Entry>(...);
basically, I want to sort the array but user can specify any field they want to sort by. My current attempt appears to be repetitive.
fun sortByOrder(){
entries.sortedWith {f: Entry, s: Entry ->
if (f.order != s.order) {
f.order - s.order
} else {
f.entryType.compareTo(s.entryType)
}
}
}
I couldn't find away of having one method like below that can do it dynamically:
fun sortEntries(fieldName: string, fieldType: string, backUpField: string){
if (fieldType == 'number'){
entries.sortedWith {f: Entry, s: Entry ->
if (f[fieldName] != s[fieldName]) {
f[fieldName] - s[fieldName]
} else {
f[backUpField].compareTo(s[backUpField])
}
}
}
}
how can I achieve something like the above?

Kotlin - adding map function conditionally

I would like to feature toggle a map function on a list. I have a map that I would like to run only if the feature is on:
So for something like this:
items
.map { doTransformation(it) }
.map { runOnlyIfFeatureIsOn(it) }
Is there a way of adding the whole .map function conditionally in kotlin, so that it is only there if it is feature toggled?
let() is handy for doing arbitrary processing in a pipeline, e.g.:
items
.map{ doTransformation(it) }
.let{ if (someCondition) it.map{ runOnlyIfFeatureIsOn(it) } else it }
(For complex/costly conditions, this will be more efficient than putting the if inside the map call, as this'll only evaluate the condition once.)
Maybe just do if in map? There is no problem with that:
val list = listOf(1, 2, 3)
list
.map { it * 2 }
.map {
if (featureIsOn) {
runFeatureMapping(it)
} else {
it
}
}
Using sequences:
var sequence = items.asSequence()
.map { doTransformation(it) }
if (<feature_1_enabled>) {
sequence = sequence.map { runOnlyIfFeature1IsOn(it) }
}
if (<feature_2_enabled>) {
sequence = sequence.map { runOnlyIfFeature2IsOn(it) }
}
val result = sequence.toList()
Sequences are lazy-evaluated and should be used when mutliple operations (filter/map/etc) are applied

Create map from list of list

I want to create map from list of list and I have write this code
fun getCourses(coursesCount: Int): Map<Course, Int> {
val paidCourses = mutableMapOf<Course, Int>()
for(student in data) {
for(course in student.subscribedCourses) {
if( course.isPaid ) {
paidCourses.putIfAbsent(course, 0)
paidCourses[course] = paidCourses[course]!! + 1
}
}
}
return paidCourses.toList().sortedByDescending { (_, value) -> value }.take(coursesCount).toMap()
}
I wonder how can I concise this more in Kotlin.
You can do a flatMap to flatten the "students with courses" to just a single list of all the courses, filter by isPaid, group by each course, and use eachCount to count the courses.
val paidCourses =
data.flatMap { it.subscribedCourses }
.filter { it.isPaid }
.groupingBy { it }.eachCount()
Note that this will create multiple intermediate lists and loop through them multiple times, which may be undesirable. Here's a way that avoids this, and is still quite concise:
val paidCourses = mutableMapOf<Course, Int>()
for(student in data) {
for(course in student.subscribedCourses) {
if (course.isPaid) {
paidCourses.merge(course, 1, Int::plus)
}
}
}
You can also do:
val paidCourses = mutableMapOf<Course, Int>()
for(student in data) {
student.subscribedCourses.filter { it.isPaid }
.groupingBy { it }
.eachCountTo(paidCourses)
}
You can use merge to increment the frequency.
paidCourses.merge(course, 1, Int::plus)

Finding an item in a list of lists that can contain many levels

kotlin 1.4.72
I have the following class that contains a list. However, the list will contain another list and could be 3 or 4 levels deep.
I am populating the data structure like this. And have a method to find a item from one of the children.
data class Producer(
val id: Int,
val children: List<Producer> = emptyList(),
) {
fun createProducer(src: Producer): Producer {
return Producer(
id = src.id,
children = src.children.map {
createProducer(it)
}
)
}
fun findProducerByIDorNull(id: Int): Producer? {
val producer = children.firstOrNull {
it.id == id
}
return producer
}
}
Currently I am using firstOrNull. However, that will only find the item in the 1st level. If the item is at a 3 level it will return null.
Just wondering if there is a better way to do this.
Many thanks for any suggestions,
You could make findProducerByIDOrNull recursive. Something like:
fun findProducerByIDorNull(id: Int): Producer? {
if (this.id == id) {
return this
}
return children.asSequence()
.mapNotNull { it.findProducerByIDorNull(id) }
.firstOrNull()
}

How do i filter and copy values to a list of objects based on another list in Kotlin

I am trying to filter a list of objects based on a certain condition from a second list and then update/copy certain values from the second list to the already filtered list.
I tried this:
val filteredList = firstObjectList.stream()
.filter { first ->
secondObjectList.stream()
.anyMatch { second ->
second.sharedId == first.shareId
}
}.toList()
filteredList.map { filtered ->
secondObjectList.forEach { so ->
if(filtered.shareId == so.shareId){
val asset= Assets()
asset.address = so.address
asset.assetValue = so.assetValue
filtered.asset = asset
}
}
}
return filteredList
here are the objects:
Class firstObject(
val shareId: Int,
var asset : Asset? = null)
Class secondObject(
val shareId: Int,
var asset: Assets)
Class Assets(
val address: String,
val assetValue: Double)
This works but obviously not very efficient and Java based. How can I improve and write this in idiomatic kotlin? as i don’t seem to be able to chain operators correctly. Thanks in Advance.
val map = secondObjectList.associateBy { it.shareId }
val filteredList = firstObjectList
.filter { it.shareId in map }
.onEach { fo ->
fo.asset = map.getValue(fo.shareId).asset.let { Assets(it.address, it.assetValue) }
}