RxKotlin COUNT with GROUP BY and return a list - kotlin

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

Related

Kotlin mapping a list that has a list inside

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

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)

Cast away nested Option in Kotlin arrow

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

kotlin using rxjava2 map operator to modify a list of users

Kotlin 1.3.31
RxJava2
I have the following method that should get a list of users and loops through using the map operator and increase the gpa by 10. Then print the result out in the onNext. However, what I am getting printed is this:
kotlin.Unit
I was thinking that the list of users should be passed down the stream to the onNext in the subscribe method
private fun getUserListMapped() {
val disposable = getUserListFromCallable()
.map {
it.forEach { user ->
user.gpa *= 10
}
}
.subscribeOn(schedulerProvider.background())
.observeOn(schedulerProvider.ui())
.subscribe { println("users $it") }
}
This is what I am doing to get my users:
private fun getUserListFromCallable(): Observable<List<User>> {
return Observable.fromCallable { createListOfUsers() }
}
private fun createListOfUsers(): List<User> {
Thread.sleep(500L) // simulate getting from the network or local
return listOf(
User("john", "paris town", 5.6F),
User("simon", "hollands place", 2.56F),
User("lisa", "london bridge", 3.89F),
User("peter", "tokyo hills", 4.3F))
}
Many thanks for any suggestions
in your map you have to return value:
.map {
it.forEach { user ->
user.gpa *= 10
}
it
}
or you can just use doOnEach like this:
.doOnEach {
it.forEach { user ->
user.gpa *= 10
}
}
forEach method returns an Unit and that is why you see Unit printed. You should change the map operator to something like this:
.map { it.map { user -> user.apply { gpa *= 10 } } }

Kotlin distinctBy with condition

I have array with multiple objects with the same key, Other objects have empty values and I was hoping to use distinctBy to remove duplicates and get objects which values has the longest string.
data class ObjA(val key: String, val value: String)
fun test() {
val listOfA = mutableListOf(
ObjA("one", ""), //This will be taken in distinctBy.
ObjA("one", "o"),
ObjA("one", "on"),
ObjA("one", "one"), //This will be ignored in distinctBy. I WANT THIS ONE.
ObjA("two", ""), //This will be returned in distinctBy
ObjA("two", "2"),
ObjA("two", "two"), //But I want this.
ObjA("three", "3"),
ObjA("four", "4"),
ObjA("five", "five")
)
val listOfAWithNoDuplicates = listOfA.distinctBy { it.key }
for (o in listOfAWithNoDuplicates) {
println("key: ${o.key}, value: ${o.value}")
}
}
Output
key: one, value:
key: two, value:
key: three, value: 3
key: four, value: 4
key: five, value: five
How to make this work. Any help will be appreciated.
As distinctBy just returns the distinct keys based on your selector (and in the order of the list), you end up with unique keys, but not yet with the values you want.
For that particular use-case I would probably just sort beforehand, followed by the distinctBy
listOfA.sortedByDescending { it.value.length }
.distinctBy { it.key }
Which creates a new list at sortedByDescending or you just sort the current list beforehand (sortByDescending) and apply distinctBy later on, e.g.:
listOfA.sortByDescending { it.value.length }
listOfA.distinctBy { it.key }
In both cases you get a new List<ObjA> with the expected values.
Several other variants come to my mind as well. All those variants will put the results into a Map<String, ObjA> where the key is actually the unique ObjA.key. You may want to call .values directly if you are not interested in the key/ObjA-mapping.
variant using groupingBy and reduce:
listOfA.groupingBy { it.key }
.reduce { _, acc, e->
maxOf(e, acc, compareBy { it.value.length })
}
variant using a plain forEach/for and filling its own Map:
val map = mutableMapOf<String, ObjA>()
listOfA.forEach {
map.merge(it.key, it) { t, u ->
maxOf(t, u, compareBy { it.value.length })
}
}
variant using fold and merge (very similar to the previous... just using fold instead of for/forEach):
listOfA.fold(mutableMapOf<String, ObjA>()) { acc, objA ->
acc.apply {
merge(objA.key, objA) { t, u ->
maxOf(t, u, compareBy { it.value.length })
}
}
}
variant using groupBy followed by mapValues (but you are then actually creating 1 map which you discard immediately):
listOfA.groupBy { it.key } // the map created at this step is discarded and all the values are remapped in the next step
.mapValues { (_, values) ->
values.maxBy { it.value.length }!!
}
You can use maxBy{} like:
val x = listOfAWithNoDuplicates.maxBy { it.key.length }
println(x)
Output
ObjA(key=three, value=3)