Why is kotlin stream evaluating an .all predicate to true where the first Iterable is empty - kotlin

I have a List of objects.
importerResponse.applications is empty (size=0)
This is my code:
val isDeployed = importerResponse.applications
.flatMap(Application::instances)
.map(Instance::state)
.all { state -> DEPLOYED == state }
isDeployed is true in this case. How can this be? I want it to resolve into false if applications is empty.

Why would you want that? All the elements in the collection satisfy your predicate.
You can check the documentation:
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/all.html
If you want you can explicitly check for the collection being empty.
This should give you what you want:
val isDeployed = importerResponse.applications
.flatMap(Application::instances)
.map(Instance::state)
.count { state -> DEPLOYED == state } > 0```

The all method might be looking for any element that doesn't meet the condition, since you don't have any, it defaults to true.
You can achieve what you want by doing something similar to this:
val isDeployed = importerResponse.applications
.flatMap(Application::instances)
.map(Instance::state)
.let { it.size() > 0 && it.all { state -> DEPLOYED == state } }
Note that let allows you to reuse the same expression without recalculating it twice.

It can be a little bit confusing, why "any" returns "false" on empty collections, but "all" return true, because "all" seems to be more limiting than "any" (based on human language).
But if you ask as one example "if all persons in a room are male", than that's still true, if the room is empty. 0 out of 0 persons are ALL.

Related

Combining Two List in Kotlin with Index

There is a data class as fruits.
data class Fruits(
val code: String, //Unique
val name: String
)
The base list indexed items with boolean variable is as below.
val indexList: MutableList<Boolean> = MutableList(baseFruitList.size) { false }
Now the Favourite Indexed list is as below
val favList: MutableList<Boolean> = MutableList(favFruitList.size) { true}
I want a combined full list which basically has the fav item indicated as true.
Ex:
baseFruitList = {[FT1,apple],[FT2,grapes],[FT3,banana],[FT4,mango],[FT5,pears]}
favList = {[FT2,grapes],[FT4,mango]}
The final index list should have
finalIndexed = {false,true,false,true,false}
How can we achieve in Kotlin, without iterating through each element.
You can do
val finalIndexed = baseFruitList.map { it in favList }
assuming, like #Tenfour04 is asking, that name is guaranteed to be a specific value (including matching case) for a specific code (since that combination is how a data class matches another, e.g. for checking if it's in another list)
If you can't guarantee that, this is safer:
val finalIndexed = baseFruitList.map { fruit ->
favList.any { fav.code == fruit.code }
}
but here you have to iterate over all the favs (at least until you find a match) looking to see if one has the code.
But really, if code is the unique identifier here, why not just store those in your favList?
favList = listOf("FT2", "FT4") // or a Set would be more efficient, and more correct!
val finalIndexed = baseFruitList.map { it.code in favList }
I don't know what you mean about "without iterating through each element" - if you mean without an explicit indexed for loop, then you can use these simple functions like I have here. But there's always some amount of iteration involved. Sets are always an option to help you minimise that

How to assign a new list to a nullable field if null or else just add an element to the existing list in Kotlin?

I have an object media that holds descriptions which is a list. I'd love to see some elegant logic in Kotlin to add an element to that descriptions if the field is not null or add a fresh new list (with an initial element) to that field if it is null.
Pseudo:
if (media.descriptions == null) { media.descriptions = listOf("myValue")}
else { media.descriptions.add("myValue") }
I would probably do it the other way around, except you need to alter media itself (see below), i.e. creating your list first and add all the other entries to that list if media.descriptions isn't null:
val myDescriptions = mutableListOf("myValue") // and maybe others
media.descriptions?.forEach(myDescriptions::add)
If you need to manipulate descriptions of media, there is not so much you can do to make it more readable...:
if (media.descriptions == null) {
media.descriptions = mutableListOf("myValue") // just using mutable to make it clear
} else {
media.descriptions += "myValue"
}
or maybe:
if (media.descriptions == null) {
media.descriptions = mutableListOf<String>()
}
media.descriptions?.add("myValue")
You can use the elvis ?: operator to assign the list.
The simplest way I can think of is
media.descriptions = media.descriptions ?: listOf("myValue")
media.descriptions.add("myValue")

How to properly sanitize a list of items received from server using RX | filter{} map{}

I have the following code which I am trying to use for two purposes:
1) Call an API and get result as a POJO
2) Sanitize this object (POJO) before I display it in the UI
private fun getWinbackDataItems(rewardPurpose: String) /*Single<WinbackBaseItem>*/ {
val x = repository.getRewardsList(rewardPurpose)
.filter {
it.result?.rewards != null
}.map { winback ->
winback.result?.rewards?.asSequence()?.filter { rewardsItem ->
rewardsItem?.id != null && rewardsItem.title != null
}?.toList()?.take(3)?.map {
WinbackListItem(it?.id, it?.title!!, false)
}?.toList()
}
}
The point of contention for me is the line below:
itemListSanitized.add(WinbackListItem(it.id, it.title, false))
At this point I assume the filter has removed all nulls from the original list but to my amazement I find that I have to null check on it and all its content while adding them to the new list.
What do I miss here, pardon my naivety as I have just begun reactive
I take it that you are working not against executing code but against your IDE's warning messages or just the ability for this code to compile. What you're probably running up against is that earlier checks for null won't necessarily allow the compiler to assume non-null values later on, because in the meantime, other code in a different thread could have run and changed the values.
So when you create a WinbackListItem, you can safely assume that certain items are not null, and yet the compiler can't be sure of this, because it can't know what else is going on in your process space. So the compiler requires that you tell it not to worry about null values (!!) or that you check the values again. This is just the way Kotlin works. It's often a PITA, but it's just how it is.
I played with the posted code just to be sure I knew what I was talking about. Here is code that I was able to run:
private fun getWinbackDataItems(rewardPurpose: String) /*Single<WinbackBaseItem>*/ {
val x = repository.getRewardsList(rewardPurpose)
.filter {
it.result?.rewards != null
}.map { winback ->
winback.result?.rewards?.asSequence().filter { rewardsItem ->
rewardsItem.id != null && rewardsItem.title != null
}.toList().take(3).map {
println(it.id)
println(it.title)
WinbackListItem(it.id!!, it.title!!, false)
}.toList()
}.count()
}
I created some very simple classes and objects to satisfy this code and let it run. Note that I took out some unnecessary '?' null checks. I played with input values until I was convinced that it.id and it.title can never be null when the WinbackListItem constructor is called. And yet, the two !! on its parameters, or something else making sure they are not null, are required given this definition of WinbackListItem that won't accept null parameter values:
class WinbackListItem(val id: Int, val title: String, val huh: Boolean)

Why does R.all with R.both does not equal R.allPass with the same arguments?

I'm just learning while doing ramda.js. Well, there are many ways to reach a goal with ramda, but there is on thing I do not understand.
I would like to check the input for an array of strings that all match one regular expression. I thought I could do it R.all(R.both(isString, isRegExp)), but it seems to deliver a true when the input is a number.
As expected R.allPass([isString, isRegExp]) gives a false with a number input.
But can anyone please explain me why R.all is returning a true? Or what and where is mistake (in thinking)?
Complete code:
var isString = R.is(String),
isMyRegExp = R.test(/^[a-z]+$/),
isMyRegExpString = R.both(isString, isMyRegExp),
isArrayOfMyRegExpStrings = R.all(isMyRegExpString),
isArrayOfMyRegExpStringsPass = R.allPass([isString, isMyRegExp]),
result = {
'all': isArrayOfMyRegExpStrings(9),
'allPass': isArrayOfMyRegExpStringsPass(9)
};
console.log(result);
// {
// all: true,
// allPass: false
// }
https://codepen.io/Eisenhardt/pen/PKLZqj
PS:
I know that I could shorten conditions with just the regexp, but there could be other situations where I need both conditions to be true. eg. isArrayOfNumber and sumOfNumbersOver50.
The second argument to R.all is expecting a list of values to test. Due to the way the function is implemented it is treating the 9 in your example as an empty list, resulting in a vacuous truth and evaluating to true.

How to check if a TypedPipe or a ValuePipe are empty in Scalding?

In Scalding, suppose you have a TypedPipe[Long] or ValuePipe[Long]. How would you go about checking whether they are empty in the most elegant/efficient way?
Currently testing the following:
val isTPEmpty: Boolean = typePipe.equals(TypedPipe.empty)
val isVPEmpty: Boolean = valuePipe.equals(EmptyValue)
Or, to make it more generic:
def isTypedPipeEmpty[A](typedPipe: TypedPipe[A]): Boolean = {
val emptyTP: TypedPipe[A] = TypedPipe.empty
typedPipe.equals(emptyTP)
}
UPDATE: this doesn't work (will return false for an empty TypedPipe). Appreciate any inputs.
After speaking to several people on this, there is no straight solution simply because a TypedPipe is distributed, and checking whether it is empty is "expensive", therefore one should avoid this as much as possible.
If you absolutely have no choice, what worked for me was something "ugly" as creating a temporary empty TypedPipe, then calling mapWithValue on my ValuePipe, and if it is empty do X, otherwise do Y. Something like:
TypedPipe.from(List()).mapWithValue(valuePipe) { case (temp, valuePipe) => if (valuePipe.isEmpty) doX else doY }
But again, cumbersome.