Kotlin functional find single element - kotlin

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

Related

In Kotlin, how can I test and use a value without computing it twice?

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
}

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)

How to hand over Boolean operator as parameter in Kotlin?

I have a function which has quite a lot lines. In that function I have a .filter{} like:
fun getMyListForFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag == Query.IS_FOO }
.map{
//..mappings
}
}
and then I have a second function just to retrieve queries which are NOT Foo:
fun getMyListForNotFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag != Query.IS_FOO }
.map{
//..mappings
}
}
As you can the only difference is the == or != operator in the .filter function. Although I have all the previous lines duplicated..
I bet there is a nice Kotlin way to enhance this code?
Pass a predicate as a parameter to your function for filtering the list.
fun getMyList(predicate: (YourType) -> Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter(predicate)
.map{
//..mappings
}
}
Usage:
val listForFoo = getMyList { it.flag == Query.IS_FOO }
val listForNotFoo = getMyList { it.flag != Query.IS_FOO }
OR, if you just want to pass a Boolean, you can also do that:
fun getMyList(filterFoo: Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter {
val isFoo = it.flag == Query.IS_FOO
if(filterFoo) isFoo else !isFoo
}
.map{
//..mappings
}
}
I would use partition directly.
I created a sample in kotlinlang.org's playground and it looks like this:
// Given a "thing"
data class Thing(val id: Int, val isFoo: Boolean)
// Have a function that simplifies this:
fun filterThings(source: List<Thing>) = source.partition { it.isFoo }
// Alternatively, you could have a more generic one:
fun filterThings(source: List<Thing>,
predicate: ((Thing) -> Boolean)) = source.partition(predicate)
// And you can use either like so:
// Given the source
val source = listOf(Thing(1, true),
Thing(2, true),
Thing(3, false),
Thing(4, true),
Thing(5, false),
Thing(6, false))
// Filter them with the non-configurable version:
val results = filterThings(source)
// or the more configurable one where *you* supply the predicate:
val results = filterThings(source) { it.isFoo }
The results are going to be:
results.first is going to be the one that pass the predicate, and the rest will be in results.second:
results.first = [Thing(id=1, isFoo=true), Thing(id=2, isFoo=true), Thing(id=4, isFoo=true)]
results.second = [Thing(id=3, isFoo=false), Thing(id=5, isFoo=false), Thing(id=6, isFoo=false)]

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

Find item from a list using nested find keyword in kotlin

I have an enum class which will hold different states of the app.
enum class State {
STATE_1, STATE_2, STATE_4, READY, UNKNOWN
}
I will have a list that contains these states in some random order. I want to write an algorithm that will return a particular state if other states are not available. For example:
val list = listOf(READY, STATE_2, STATE_1)
return STATE_2
val list = listOf(READY, STATE_1)
return STATE_1
val list = listOf(STATE_2, STATE_1)
return STATE_2
val list = listOf(UNKNOWN, STATE_2)
return STATE_2
I am searching for something that will help me do nested find over a collection in kotlin.
This is what I have achieved till now:
private fun filter(states: List<State>): State {
val currentStates = states.filter {
it != State.UNKNOWN || it != State.READY
}
currentStates.find { it == State.STATE_4 }?.let {
return it
} ?: currentStates.find { it == State.STATE_2 }?.let {
return it
} ?: currentStates.find { it == State.STATE_1 }?.let {
return it
}
}
If you define your enum states in order by what "wins" the filter:
enum class State {
READY, UNKNOWN, STATE_1, STATE_2, STATE_4
}
Then you can pick the one with the highest ordinal:
fun filter(states: List<State>): State =
states.maxBy(State::ordinal) ?: error("Must have at least one state")
This assumes there is at least one State in the provided list. If that's not a safe assumption, you can return a nullable:
fun filter(states: List<State>): State? = states.maxBy(State::ordinal)
If there's some reason you can't rely on defining them in a certain order, you can provide the order as a list (or linked Set to make it fool-proof) in this function:
fun filter(states: List<State>): State {
val order = linkedSetOf(State.READY, State.UNKNOWN, State.STATE_1, State.STATE_2, State.STATE_4)
assert(order.size == State.values().size) // To ensure this function is updated if States are updated.
return states.maxBy { order.indexOf(it) } ?: error("Must have at least one state")
}
I doubt there is much you can optimize. I can only think of using loop over predefined priorities like following
private val PRIORITY = listOf(State.STATE_4, State.STATE_2, State.STATE_1)
private fun filter(states: List<State>): State? {
val currentStates = states.filter {
it != State.UNKNOWN || it != State.READY
}
PRIORITY.forEach { prio ->
currentStates.find { it == prio }?.let {
return it
}
}
return null
}
Note: I'm using nullable State? as return type instead of State for the cases when nothing appropriate is found within a collection.