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)
Related
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)
}
}
Every so often, I find myself wanting to compute a value for some sort of filter operation, but then wanting to use that value when it's already disappeared into the condition-checking thing.
For instance:
val found = list.firstOrNull { slowConversion(it).isWanted() }
if (found != null) {
something(found, slowConversion(found))
}
or
when {
other_conditions -> other_actions
list.any { it.contains(regex1) } -> something(list.firstOrNull { it.contains(regex1) } ?: "!!??")
}
For the slowConversion() I can work with a sequence mapped to pairs, although the terms first and second kinda confuse things a bit...
val pair = list.asSequence().map { it to slowConversion(it) }.firstOrNull { it.second.isWanted() }
if ( pair != null ) {
something(pair.first, pair.second)
}
or if I only want the conversion,
val converted = list.firstNotNullOfOrNull { slowConversion(it).takeIf { it.isWanted() } }
but the best I can come up with to avoid the when duplication involves moving the action part into the condition part!
fun case(s: List<String>, r: Regex) {
val match = s.firstOrNull { it.contains(r) }?.also { something(it) }
return match != null
}
when {
other_conditions -> other_actions
case(list, regex1) -> true
}
At this point, it seems I should just have a stack of function calls linked together with ||
other_things || case(list, regex1) || case(list, regex2) || catchAll(list)
Is there something better or more concise for either of these?
You can write your first example like this:
for(element in list) {
val result = slowConversion(element)
if(result.isWanted()) {
something(element, result)
break
}
}
This might not look very Kotlin-ish, but I think it's pretty straightforward & easy to understand.
For your second example, you can use the find function:
when {
other_conditions -> other_actions
else -> list.find { it.contains(regex1) }?.let(::something)
}
If you have multiple regexes, just iterate over them,
val regexes = listOf(regex1, regex2, ...)
for(regex in regexes) {
val element = list.find { it.contains(regex1) } ?: continue
something(element)
break
}
I am relatively new to Kotlin and I try to overcome a special case.
I am filtering a books store and want to verify that the length of the obtained list is exactly one unit shorter than the original one. Further I need to verify that the discarded element is under a specific state. Here is my example:
fun BookStoreVerified(bookStore: BookStore): Boolean {
val specialChapter = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }.filter { it != null && it.state == Chapter.SPECIAL }
val total = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filterNotNull()
val finalChapters = book.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filter { it != null && it.state.isCorrect }
return (finalChapters.size + specialChapterFigure.size == total.size) && (specialChapter.size == 1)
}
My question is if there is a smarter way to compute the above operation. I would like to know if ander a scope like filter, map can we make reference to the previous object? ( get the length of the original list for instance ?)
You have Books where each Book contains a list of Chapters. You want to partition chapters from all the books according to some criteria.
With this in mind the partition function can be useful:
data class Chapter(val state: String)
data class Book(val chapters: List<Chapter>? = null)
fun main() {
val books = listOf(
Book(),
Book(chapters = listOf(Chapter("a"), Chapter("SPECIAL"))),
Book(chapters = listOf(Chapter("c"), Chapter("d")))
)
val (specialChs, regularChs) = books
.flatMap { it.chapters ?: emptyList() }
.partition { it.state == "SPECIAL" }
println(specialChs) // [Chapter(state=SPECIAL)]
println(regularChs) // [Chapter(state=a), Chapter(state=c), Chapter(state=d)]
}
Now that you have specialChs and regularChs, you can check whatever invariants you want.
For example:
check(specialChs.size == 1 && specialChs.first().state ==
"SPECIAL")
Edit: It is possible to abstract away the existence of null chapters inside a Book:
data class Book(val chapters: List<Chapter>? = null) {
val safeChapters: List<Chapter>
get() = chapters ?: emptyList()
}
then in your code you can flatMap { it.safeChapters } instead of .flatMap { it.chapters ?: emptyList() }
I have a list of recurring elements in Kotlin, say:
val result = arrayListOf<String>("AA", "BB", "CC", "AA", "BB")
I would like to group them by their value along with how many times they appear, so the output would be pairs of:
{"AA", 2}, {"BB", 2}, {"CC", 1}
I have resolved the problem using in Kotlin as follows:
val ans = result.map { it.value }
.groupBy { it }
.map { Pair(it.key, it.value.size) }
.sortedByDescending { it.second }
I want to write same code in RxKotlin for learning and tried with the following but do not know how to apply map/flatMap to achieve the result.
val source = Observable.fromIterable(result)
source.groupBy{ it }.subscribe { showresult(it) }
Try something like this:
source.groupBy { it }
.flatMapSingle { g -> g.count().map { Pair(g.getKey(), it) } }
.toSortedList { a, b -> b.second.compareTo(a.second) }
.subscribe { list -> println(list) }
I have a value with below type in my data class
Option<Option<List<Pair<String, Option<String>>>>>
How would I access the right-most Option<Sring>. I have tried with when expression like below
when(Option<Option<List<Pair<String, Option<String>>>>>) {
is Some -> when(Option<Option<List<Pair<String, Option<String>>>>>.t) {
is Some -> when(Option<List<Pair<String, Option<String>>>>.t) {
......
but it's not looking good. Is there any other way to cast away those Options
val t: Option<Option<List<Pair<String, Option<String>>>>> =
Some(Some(listOf(
Pair("a", Some("A")),
Pair("b", Some("B")),
Pair("c", None)
)))
val l = t.flatten()
.map { list ->
list.map { pair -> pair.second }
}
.getOrElse { emptyList() }
val first = l.getOrElse(0) { None }
val second = l.getOrElse(1) { None }
val missing = l.getOrElse(7) { None }
nested // Option<Option<List<Pair<String, Option<String>>>>>
.flatten() // Option<List<Pair<String, Option<String>>>>
.map { it.map { it.second() } } // Option<List<Option<String>>>
.sequence(Option.applicative()) // Option<Option<List<String>>>
.flatten() // Option<List<String>>
flatten gets rid of nested options, and sequence goes from List<Option<A>> to Option<List<A>>.