Missing return in a function expected to return 'Room' - func

func getRoomById(id: Int?) -> Room {
//void change Room ?
for room in allRooms {
if room.id == id {
return room
}
}
}
Why this case I have this problem ? I want get room with id (Int). I have a building,floor,flat and room.

Related

Kotlin functional find single element

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

Optimizing a for loop to add items to a map

Kotlin 1.5.42
I have the following data class and I need to filter out level 2 and level 3 values. Then I use a for..loop to loop through the level2 and check that the level3 has a parentId that matches the level2 value
Which is added to a list. When that level3 loop has finished I add to map with the key being the level2 object and the value being the list of level3.
i.e.
Map<TopsProductFilterItem, List<TopsProductFilterItem>>
I was looking for a better solution that is more concise using kotlin and looking if associateBy, or assoicateWith would help.
data class TopsProductFilterItem(
val value: String = "",
val catalogSearchCustomAttribute: CatalogSearchCustomAttribute = CatalogSearchCustomAttribute(),
)
And the following data class that has the levels
data class CatalogSearchCustomAttribute(
val level: Int = 0,
val parentId: Int = 0)
As there can be many levels assigned to his class I am only interested in level 2 and level 3. So I have filtered them out as below.
private fun createMapOfLevelCategories(listOfTopsProductFilterItem: List<TopsProductFilterItem>) {
val listOfLevel2 = listOfTopsProductFilterItem.filter { topsProductFilterItem ->
topsProductFilterItem.catalogSearchCustomAttribute.level == 2
}
val listOfLevel3 = listOfTopsProductFilterItem.filter { topsProductFilterItem ->
topsProductFilterItem.catalogSearchCustomAttribute.level == 3
}
val mapOfCategoryLevel2 = mutableMapOf<TopsProductFilterItem, List<TopsProductFilterItem>>()
listOfLevel2.forEach { categoryLevel2 ->
/* Find the parent id in the level 3 and from the level 2 value */
val listOfCategoryLevel3 = mutableListOf<TopsProductFilterItem>()
listOfLevel3.forEach { categoryLevel3 ->
if(categoryLevel2.value.toInt() == categoryLevel3.catalogSearchCustomAttribute.parentId) {
/* found a matching parent ID and value */
listOfCategoryLevel3.add(categoryLevel3)
}
}
mapOfCategoryLevel2.put(categoryLevel2, listOfCategoryLevel3)
}
}
You can do listOfLevel2.associateWith { ... }. associatedWith creates map with the keys being the same as the iterable you called it on, but allows you to specify how you want each key's associated value to be transformed. In this case, we want the value to be all the items in the level 3 list that have a parentId equal to the key's value.toInt().
listOfLevel2.associateWith { level2 ->
listOfLevel3.filter { level3 ->
level2.value.toInt() == level3.catalogSearchCustomAttribute.parentId
}
}
Even better, you can do a groupBy on the level 3 parent Ids first, then you don't have to loop over the the listOfLevel3 over and over again in associatedWith:
listOfLevel3.groupBy { it.catalogSearchCustomAttribute.parentId }.let { parentIdGroups ->
listOfLevel2.associateWith { level2 ->
parentIdGroups[level2.value.toInt()] ?: emptyList()
}
}
It may be a matter of taste regarding code readability, but I would do it like this:
private fun createMapOfLevelCategories(listOfTopsProductFilterItem: List<TopsProductFilterItem>): Map<TopsProductFilterItem, List<TopsProductFilterItem>> {
// create map: parentId -> (parent, children)
val resultById = listOfTopsProductFilterItem
.filter { it.catalogSearchCustomAttribute.level == 2 }
.associate {
it.value.toInt() to Pair(it, mutableListOf<TopsProductFilterItem>())
}
// associate children to parents
listOfTopsProductFilterItem
.filter { it.catalogSearchCustomAttribute.level == 3 }
.forEach {
resultById.getValue(it.catalogSearchCustomAttribute.parentId).second += it
}
return resultById.values.toMap()
}
I wouldn't say it is trivial to read and understand, but at least for me it is cleaner than your implementation. It should be also a little more performant, because we iterate over level3 items only once, not once per level2 item.
There is a balance between concise and readable that is subjective. This works for me:
fun mapOfLevel2Categories(itemList: List<TopsProductFilterItem>): Map<TopsProductFilterItem, List<TopsProductFilterItem>> {
fun List<TopsProductFilterItem>.withParent(item: TopsProductFilterItem) =
this.filter { it.catalogSearchCustomAttribute.parentId == item.value.toInt() }
val (level2items, level3items) = itemList
.filter { it.catalogSearchCustomAttribute.level in (2..3) }
.partition { it.catalogSearchCustomAttribute.level == 2 }
return level2items.associateWith { level3items.withParent(it) }
}
By the way, having the string TopsProductFilterItem.value as the id is odd. I am guessing that this is due to simplifying the actual implementation for SO.
This can be achieved in one statement-chain using associateWith.
fun createMapOfLevelCategories(
listOfTopsProductFilterItem: List<TopsProductFilterItem>,
): Map<TopsProductFilterItem, List<TopsProductFilterItem>> {
val upperLevel = 2
val lowerLevel = 3
return listOfTopsProductFilterItem
// find all upper-level items
.filter { it.catalogSearchCustomAttribute.level == upperLevel }
// find all children for the upper-level item
.associateWith { upperLevelItem ->
// search the item list for children of the upperLevelItem...
listOfTopsProductFilterItem
// find all lower-level items
.filter { it.catalogSearchCustomAttribute.level == lowerLevel }
// find all lower-level items with a parent in the upper-level
.filter { it.catalogSearchCustomAttribute.parentId == upperLevelItem.value.toInt() }
}
}
If searching filtering through the list repetitively is expensive, then use groupBy to create a Map. Then all children for a given parentId can be easily fetched.
fun createMapOfLevelCategories(
listOfTopsProductFilterItem: List<TopsProductFilterItem>,
): Map<TopsProductFilterItem, List<TopsProductFilterItem>> {
val upperLevel = 2
val lowerLevel = 3
// group all lower-level Items by parentId
val mapParentIdToChildItems: Map<Int, List<TopsProductFilterItem>> =
listOfTopsProductFilterItem
.filter { it.catalogSearchCustomAttribute.level == lowerLevel }
.groupBy { it.catalogSearchCustomAttribute.parentId }
return listOfTopsProductFilterItem
// find all upper-level items
.filter { it.catalogSearchCustomAttribute.level == upperLevel }
// find all children for the upper-level item
.associateWith { upperLevelItem ->
mapParentIdToChildItems[upperLevelItem.value.toInt()] ?: listOf()
}
}
Assumptions
I've made two assumptions,
TopsProductFilterItem.value is a unique identifier,
The relationship is one parent has many children, not many-to-many.

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.

RxJava how to group items of one list to Map<Key, List<Value>>

I have a class "Cabinet" with such structure:
class Cabinet {
var title: String
var floor: Int
var cabinetNumber: Int
}
And another class "Tabs"
class Tabs {
var floor: Int
var cabinetList: List<Cabinet>
}
So with help of RxJava I'm trying to create Observable<List<Tabs>>.
Firstly I get a List of Cabinet, so I have to get all "cabinets" to "Tabs" structure.
fun getCabinets(): Observable<List<Tabs>> {
return getCabs() //This metod returns List<Cabinet>
.observeOn(Schedulers.io))
.filter { it.title != null }
... ??? //There I don't get what to do next
I thought to use "toMap" to create collection Map<floor: Int, cabinetList: List<Cabinet>> but don't know how to make it reqursively.
Please help
To put your cabinets into a Map you could utilize collectInto operator:
getCabs()
.filter { it.title != null }
.collectInto(mutableMapOf<Int, MutableList<Cabinet>>()) { map, cabinet ->
if(map.contains(cabinet.floor)) map[cabinet.floor]?.add(cabinet)
else map.put(cabinet.floor, mutableListOf(cabinet))
}
// here is your Map<Int, List<Cabinet>>
Going further, you could map your Map to List<Tab> using flatMapIterable and Set of Map entries:
.toObservable() // because collectInto returns Single
.flatMapIterable { it.entries }
.map { Tab(floor = it.key, cabinetList = it.value) }
// here is your Observable<List<Tabs>>
P.S. I assume that you use RxJava2.

RxJava2 Maybe return empty Observable if no element

Is there a better / more idiomatic way to use a Maybe type from JavaRx 2 than flatMap and try/catch? The following example takes a Maybe<User> and tries to book them a random ticket for a flight. If the user doesn't exist, return an empty Observable.
fun bookRandomTicketFor(userId: UUID): Observable<Ticket> {
val agencies = travelAgents() // Observable<TravelAgency>
val user = findById(userId) // Maybe<User>
val location = locate() // Observable<GeoLocation>
return Observable
.just(user.toObservable())
.flatMap { usr ->
try {
usr.zipWith(location, { aUser, location ->
agencies
.flatMap { agency ->
agency
.search(aUser, location) // Observable<Flight>.
.toList() // Convert to List<Flight>.
.toObservable() // And to Observable<List<Flight>>.
.flatMap { flights -> // So it can be shuffled,
Observable.just( // giving a random order.
shuffle(flights as MutableList<Flight>)[0]
)
}
}.firstElement() // Now take the first randomly shuffled Flight.
}).flatMap { flight ->
book(user.toObservable(), flight.toObservable())
}
} catch (ex: Exception) {
Observable.empty<Ticket>()
}
}
.doOnSubscribe { Logger.log("Random ticket: start for $userId") }
.doOnComplete { Logger.log("Random ticket: exit for $userId") }
}
It seems a bit of a fudge to have to convert the Maybe<User> to an Observable and start with an Observable<Observable<User>> that I can then flatMap and try/catch. Just wondering if there is a neater approach to doing this?