How figure out multiple nested if else condition? - kotlin

I am not getting an efficient way to check below mentioned condition.
What I want to achieve is that-
There are some processes that need to be done if their corresponding boolean is true suggesting to start the process. So I want to check if a particular condition is done only if it should be started.
There are some boolean variables
var shouldStartProcessA
var shouldStartProcessB
var shouldStartProcessC
var isADone
var isBDone
var isCDone
if (shouldStartProcessA && shouldStartProcessB && shouldStartC) {
if (isADone && isBDone && isCDone) {
// Every process completed
}
}
if (shouldStartProcessA && shouldStartProcessB) {
if (isADone && isBDone) {
// Every process completed
}
}
if (shouldStartProcessA && shouldStartC) {
if (isADone && isCDone) {
// Every process completed
}
}
if (shouldStartProcessB && shouldStartC) {
if (isBDone && isCDone) {
// Every process completed
}
}
if (shouldStartProcessA) {
if (isADone) {
// Every process completed
}
}
if (shouldStartProcessB) {
if (isBDone) {
// Every process completed
}
}
if (shouldStartProcessC) {
if (isCDone) {
// Every process completed
}
}
This type of validating condition grows exponentially by introducing every other boolean. I am struggling to find a straightforward implementation to check these conditions.

Instead of doing things this way, I'd recommend a data structure that allows you to add tasks and check their state. There are a lot of ways to do that, but the basic idea is you can iterate over all the items and use functions like all to confirm they're all in the appropriate state. That way you don't have to hand-wire everything together
You could use a Map and add tasks to it, initially mapping them to false and setting that to true when they're completed. Or create a Set and add your tasks to that (I'm assuming you want one of each at most), and remove them / move them to a "done" list when they complete. That kind of idea. You could create an enum class to represent your tasks if you want, so each one is its own instance (e.g. Process.A, like having a dedicated, fixed variable) and you can easily use those in your logic
If you really want variables for each process, instead of a data structure, I'd still recommend rolling each pair into a single state, something like this:
enum class State {
UNUSED, PENDING, DONE
}
var processA = State.UNUSED
var processB = State.PENDING
// etc
// you can easily check them like this:
// create a list of all the variables you want to check - we're using references
// to the properties themselves (with the ::), not the current value!
val allProcesses = listOf(::processA, ::processB)
// now you have that collection, you can easily iterate over them all
// and work out what's what - we need to use get() to get the current values
val allFinished = allProcesses
.filterNot { it.get() == State.UNUSED } // ignore unused processes
.all { it.get() == State.DONE } // check ALL the required ones are DONE
You could write that check there as a single all condition, but the point is to show you you can be flexible with it, and build up your logic by filtering out the stuff you're not interested in, if you create a useful set of states
If you really do want to (or have to) stick with the current "pairs of variables" setup, you can do something similar:
// wiring them up again, creating a list of Pairs so we can iterate over them easily
val allProcesses = listOf(
::shouldStartProcessA to ::isADone,
::shouldStartProcessB to ::isBDone,
::shouldStartProcessC to ::isCDone
)
// gotta check 'em all - only returns true if that ALL meet the condition
val allComplete = allProcesses.all { (shouldStart, isDone) ->
// the get() syntax is awkward, but basically for everything we're checking
// if it either doesn't need to start, or it does but it's also done
!shouldStart.get() || (shouldStart.get() && isDone.get())
}
so adding new processes is just a case of adding their variables to the list of pairs, and they get included in the checking
You don't need the property reference stuff (::/.get()) if you create the lists right before you check them, but if you want to define them once in advance (and the property values can change after that) then that's how you'd do it. Otherwise you can just do the normal shouldStartProcessA to isADone etc, which is probably fine for most cases - I'm showing the reference stuff as a more general example for handling this kind of thing

I suppose, you should create two lists of Boolean and add variables consequently.
val list1 = listOf(shouldStartProcessA, shouldStartProcessB, shouldStartC)
val list2 = listOf(isADone, isBDone, isCDone)
Then iterate over both lists and check that items in corresponding positions have the same values.
var n = 0
for (i in list1.indices) {
if (list1[i] == list2[i]) {
n++
} else {
n = 0
break
}
}
if (n > 0) {
// Every process completed
}

Related

MutableList of MutableLists in Kotlin: adding element error

Why I'm getting "java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0" while running next code??? :
val totalList = mutableListOf<MutableList<Int>>()
fun main() {
for (i in 0..15) {
for (j in 0..10) {
*some operations and calculations with **var element of type Int***
totalList[i].add(element)
}
}
}
I was thinking that in such case while iterating through 'j' it should add elements to mutableList[i], after this it should start adding elements to mutableList[i + 1] etc.... But instead I am recieving IndexOutOfBoundsException....
val totalList = mutableListOf<MutableList<Int>>()
All this does is create one list which is going to contain MutableList<Int> items. Right now, there's nothing in it (you've supplied no initial elements in the parentheses).
Skip forward a bit, and you do this:
totalList[0].add(element)
You're trying to get the first element of that empty list and add to it. But there is no first element (index 0) because the list is empty (length 0). That's what the error is telling you.
There's lots of ways to handle this - one thing you could do is create your lists up-front:
// create the 16 list items you want to access in the loop
// (the number is the item count, the lambda generates each item)
val totalList = MutableList(16) { mutableListOf<Int>() }
// then refer to that list's properties in your loop (no hardcoded 0..15)
for (i in totalList.indices) {
...
// guaranteed to exist since i is generated from the list's indices
totalList[i].add(element)
}
Or you could do it the way you are now, only using getOrElse to generate the empty list on-demand, when you try to get it but it doesn't exist:
for (i in 0..15) {
for (j in 0..10) {
// if the element at i doesn't exist, create a list instead, but also
// add it to the main list (see below)
totalList.getOrElse(i) {
mutableListOf<Int>().also { totalList.add(it) }
}.add(element)
}
}
Personally I don't really like this, you're using explicit indices but you're adding new list items to the end of the main list. That implicity requires that you're iterating over the list items in order - which you are here, but there's nothing enforcing that. If the order ever changed, it would break.
I'd prefer the first approach - create your structure of lists in advance, then iterate over those and fill them as necessary. Or you might want to consider arrays instead, since you have a fixed collection size you're "completing" by adding items to specific indices
Another approach (that I mentioned in the comments) is to create each list as a whole, complete thing, and then add that to your main list. This is generally how you do things in Kotlin - the standard library contains a lot of functional tools to allow you to chain operations together, transform things, and create immutable collections (which are safer and more explicit about whether they're meant to be changed or they're a fixed set of data).
for (i in 0..15) {
// map transforms each element of the range (each number) to an item,
// resulting in a list of items
val items = (0..10).map { j ->
// do whatever you're doing
// the last expression in the lambda is its resulting value,
// i.e. the item that ends up in the list
element
}
// now you have a complete list of items, add them to totalList
totalList.add(items)
}
(Or you could create the list directly with List(11) { j -> ... } but this is a more general example of transforming a bunch of things to a bunch of other things)
That example there is kinda half and half - you still have the imperative for loop going on as well. Writing it all using the same approach, you can get:
val totalList = (0..15).map { i ->
(0..10).map { j ->
// do stuff
element
}
}
I'd probably prefer the List(count) { i -> ... } approach for this, it's a better fit (this is a general example). That would also be better since you could use MutableList instead of List, if you really need them to be mutable (with the maps you could just chain .toMutableList() after the mapping function, as another step in the chain). Generally in Kotlin, collections are immutable by default, and this kind of approach is how you build them up without having to create a mutable list etc. and add items to it yourself

How to remove element from iterator if condition depends on property of the object the iterator is based on?

Let me elaborate:
I need to be able to iterate over a list of objects. Each of the objects has a property which is a list, and I have to check if that list contains any elements that are not in another list.
When I tried to do it by using nested for loops, it kept giving me concurrent modification exceptions, so I tried to use an iterator, but now I'm stuck, since if I make an iterator based on the list of objects, I can't access the individual object's properties to then iterate over.
Here's some example code of what I was trying to accomplish:
for (preference in preferencesWithRestaurant) {
for (restaurantID in preference.restaurantIDs) {
// One method I tried using
preferencesWithRestaurant.removeIf{ !listOfIds.contains(restaurantID) }
/* alternate method I tried using
if (!listOfIds.contains(restaurantID)) {
preferencesWithRestaurant.remove(preference)
}
*/
}
}
If you can replace the value of preferencesWithRestaurant or store the result in another variable then you can filter it:
preferencesWithRestaurant = preferencesWithRestaurant.filter { preference ->
preference.restaurantIDs.all { it in listOfIds }
}
Depending on the exact type of preferencesWithRestaurant you may need to convert it to the proper type, e.g. invoke toMutableList() at the end.
If you prefer to modify preferencesWithRestaurant in-place, then you can use retainAll() (thanks #Tenfour04):
preferencesWithRestaurant.retainAll { preference ->
preference.restaurantIDs.all { it in listOfIds }
}
Alternatively, you can keep your original approach, but use a mutable iterator to remove an item while iterating:
val iter = preferencesWithRestaurant.listIterator()
for (preference in iter) {
for (restaurantID in preference.restaurantIDs) {
if (!listOfIds.contains(restaurantID)) {
iter.remove()
break
}
}
}

Iterate through a set and print only one message instead of one for each item on set

I'm trying to iterate through a set to find an item. If the item is found, I want it to print a certain message and another message if item is not found. So far, it works but it print a message for each item on the set, whereas I only want one message to display: either if the item was found with the price or the message that it wasn't found. I understand this is happening because of the for loop but I'm not sure how to get it to display the not found message only once and iterate through the set all the same.
This is the code:
fun getArticleOut(code:String) {
fun onSuccess(price: Int): String {
return "Price is $price"
}
fun onError(): String {
return "Article not found"
}
for (i in house.articles) {
if (i.code.equals(code)) {
val price = calculatePrice(
articleType = i.articleType,
totalTime = i.totalTime.toInt(),
hasCard = !i.hasCard.isNullOrEmpty()
)
println(onSuccess(price))
house.articles.remove(i)
} else {
println(onError())
}
}
}
Just to clarify:
data class House(val articles: MutableSet<Articles>)
data class Articles(val code: String,
var articleType: ArticleType,
var totalTime: Calendar,
var hasCard:String?=" ")
The direct answer is the break statement, which breaks out of a for or while loop.  You'd then have to move the onError() call outside the loop, with some way of telling whether the loop completed or not.  For example:
var found = false
for (i in house.articles) {
if (i.code == code) {
val price = calculatePrice(
articleType = i.articleType,
totalTime = i.totalTime.toInt(),
hasCard = !i.hasCard.isNullOrEmpty())
println(onSuccess(price))
house.articles.remove(i)
found = true
break
}
}
if (!found)
println(onError())
If you don't need to do anything after both cases (as in the code in question), then you could simplify it to return, and avoid the flag:
for (i in house.articles) {
if (i.code == code) {
val price = calculatePrice(
articleType = i.articleType,
totalTime = i.totalTime.toInt(),
hasCard = !i.hasCard.isNullOrEmpty())
println(onSuccess(price))
house.articles.remove(i)
return
}
}
println(onError())
However, there are probably better approaches that don't need manual iteration.  Kotlin's standard library is so powerful that any time you find yourself writing a loop, you should stop and ask whether there's a library function that would make it simpler.
In particular, you could use find(), e.g.:
val article = house.articles.find{ it.code == code }
if (article != null) {
val price = calculatePrice(
articleType = article.articleType,
totalTime = article.totalTime.toInt(),
hasCard = !article.hasCard.isNullOrEmpty())
println(onSuccess(price))
house.articles.remove(article)
} else {
println(onError())
}
That makes the code easier to read, too.  (Note that the code is now saying what it's doing, not how it's doing it, which is usually an improvement.)
There are also deeper design questions worth asking, which could lead to further simplifications.  For example:
If code is a unique identifier for Article, another option would be to make articles a Map from code to the corresponding Article; both checking and removal would then be constant-time operations, so more efficient as well as more concise.  (Of course, that depends on how often you're doing these lookups, and what else is setting or using articles.)
Or you could override Article.equals() to check only the code.  Then you could create a dummy Article with the code you're looking for, and do a simple in test (which uses the set's contains method) to check for its presence.  Accessing and removing the ‘true’ one in the set would be harder, though, so that may not be a good fit.
Would be neater for calculatePrice() to be defined to take an Article directly?  (Obviously that depends on whether it could be calculating the price of anything else too.)  Could it even be a method or extension function on Article?  (That probably depends whether the price is conceptually a property of the article itself, or whether it's specific to the getArticleOut() function and any surrounding code.)
Also worth pointing out that the code in the question has a nasty bug (which all these changes also work around), which is that it's trying to modify a collection while iterating through it, which is dangerous!
If you're lucky, you'll get an immediate ConcurrentModificationException showing you exactly what went wrong; if you're less lucky it'll continue but do something unexpected, such as skipping over an element or giving an apparently-unrelated error later on…
Which is another reason to avoid manual iteration where possible.
(The only safe way to remove an element while iterating is to manage the Iterator yourself, and use that to do the removal.)

Kotlin ConflatedBroadcastChannel.offer() doesn't work?

I am sending a value via MyRepository.myConflatedChannel.offer(myvalue).
I then expect to receive it in collect { } or onEach { } blocks in my ViewModel. However, neither function is invoked. It is as if nothing is passed down the ConflatedBroadcastChannel.
Has anybody seen a similar problem?
Make sure you properly work with receiving values.
If you use the ConflatedBroadcastChannel, you can use either OpenSubscription to get a ReceiveChannel or you can represent it as flow (with asFlow).
Note that consume and consumeEach are terminal, they perform an action and then cancel the channel after the execution of the block. See this.
First case:
val receivingChannel = MyRepository.myConflatedChannel.openSubscription()
// then you can consume values using for example a for loop, e.g.:
launch {
for (value in receivingChannel) {
// do something
}
}
Second case:
val receivingFlow = MyRepository.myConflatedChannel.asFlow()
launch {
receivingFlow.collect {
// do something
}
}

How I can delete duplicates in a Object list by date in Kotlin with collection operators?

I have a object list with the same id, then, i want to keep the one with the most recent date, and the delete the other with the kotlin collection operators. For Example I have :
{"id":111,
"date":"02/12/2017"
}
and the other
{"id":111,
"date":"02/8/2018"}
In this case, i would like to delete the first object.
You can achieve it like this
list.groupBy { it.id }.entries.map { it.value.maxBy { it.date } }
it will create a map of id, List<object> while keeping original order and then choose newest object from the list.
Here I am assuming date is long value timestamp
If you really must remove the entries from the current collection the following may help you (I assume your objects (Obj) are contained in a list called list):
list.removeAll { anObj -> list.any { anObj.id == it.id && it.date > anObj.date } }
// or same with removeIf
It's probably easier just collecting what you are actually interested in and then have an immutable list in the first place.
Collecting the elements you are interested in could be done as follows (there are of course lots of other ways to do that... just one of many):
val result = list.groupBy { it.id }.values.mapNotNull { it.maxBy { it.date } } // mapNotNull is only used due to maxBy returning a nullable type... it isn't really needed... or well... depends on what your date type is ;-)
result will then be a List<Obj>. Instead of mapNotNull you could also use it.maxBy { it.date }!! if you know there is at least 1 element.
If you then still need to remove the elements from the list, you could do the following:
list.removeIf { it !in result }
// or
list.removeAll { it !in result }
However I can't really recommend that you mutate your current list just for that... The result in the above example already contains all the elements in the form you want (i.e. List<Obj>). Instead rather use the benefit of having an immutable list :-)