Kotlin nested for loops to asSequence - kotlin

I'm trying to convert my nested for loop to asSequence in Kotlin. Here, my goal is to get and update the value of all my object array from another object array with the same key.
nested for loop:
val myFields = getMyFields()
val otherFields = getOtherFields()
for (myField in myFields) { // loop tru the my fields
for (otherField in otherFields) { // find the same fields
if (myField.key == otherField.key) { // if the same, update the value
val updatedMyField = myField.copy(value = otherValue.value)
myFields[myFields.indexOf(myField)] = updatedMyField // update my field value
break
}
}
}
What I've tried:
val updatedMyFields = getMyFields().asSequence()
.map { myField ->
getOtherFields().asSequence()
.map { otherField ->
if (myField.key == otherField.key) {
return#map otherField.value
} else {
return#map ""
}
}
.filter { it?.isNotEmpty() == true }
.first()?.map { myField.copy(value = it.toString()) }
}
.toList()
but this does not compile as it will return List<List<MyField>>.
I'm just looking for something much cleaner for this.

As comments suggest, this would probably be much more efficient with a Map.
(More precisely, a map solution would take time proportional to the sum of the list lengths, while the nested for loop takes time proportional to their product — which gets bigger much faster.)
Here's one way of doing that:
val otherFields = getOtherFields().associate{ it.key to it.value }
val myFields = getMyFields().map {
val otherValue = otherFields[it.key]
if (otherValue != null) it.copy(value = otherValue) else it
}
The first line creates a Map from the ‘other fields’ keys to their values.  The rest then uses it to create a new list from ‘my fields’, substituting the values from the ‘other fields’ where present.
I've had to make assumptions about the types &c, since the code in the question is incomplete, but this should do the same.  Obviously, you can change how it merges the values by amending the it.copy().
There are likely to be even simpler and more efficient ways, depending on the surrounding code.  If you expanded it into a Minimal, Complete, and Verifiable Example — in particular, one that illustrates how you already use a Map, as per your comment — we might be able to suggest something better.

Why do you want to use asSequence() ? You can go for something like that:
val myFields = getMyFields()
val otherFields = getOtherFields()
myFields.forEach{firstField ->
otherFields.forEach{secondField ->
if (firstField.key == secondField.key) {
myFields[myFields.indexOf(firstField)] = secondField.value
}
}
}
This will do the same job than your nested for loop and it's easier to read, to understand and so to maintain than your nested asSequence().

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 filter elements in one list by a property value not present in elements in another list?

I have the following code snippet
val cachedNews = listOf(News(9, "https://009"), News(8, "https://234"), News(7, "https://345"))
val freshNews = listOf(News(1, "https://123"), News(2, "https://234"), News(3, "https://345"))
val result = freshNews.filter {fresh -> filter(cachedNews, fresh)}
private fun filter(cached: List<News>, fresh: News): Boolean {
cached.forEach { cachedItem ->
if (cachedItem.url == fresh.url) return true
}
return false }
When the code runs if cachedItem.url == fresh.url the list is filtered and the result is a list where the urls of the two lists are identical. However when i reverse equality like so cachedItem.url != fresh.url the list is not filtered at all. The sequence of execution changes.
When using the == sign, the first item of freshNews is compared with the first Item of cachedNews after that the secondItem of freshNews is compared with secondItem of cachedNews and so on.
When I use the != sign the all items of freshNews are compared against only the firstItem of cachedNews ??
Am I missing something or is my code just wrong?
I'm not sure what the specific problem is because your approach is quite confusing. Your custom filter function is actually more like a contains function.
What might be useful is to:
Extract the cached URLs to a set
Filter the new results by URLs that are not in the set.
fun main() {
val cachedNews = listOf(News(9, "https://009"), News(8, "https://234"), News(7, "https://345"))
val freshNews = listOf(News(1, "https://123"), News(2, "https://234"), News(3, "https://345"))
val cachedUrls = cachedNews.map { it.url }.toSet()
val result = freshNews.filterNot { cachedUrls.contains(it.url) }
println(result)
}
Result:
[News(id=1, url=https://123)]

How to combine a lot of LiveDatas together and transform into a single entity?

I am looking to find a way to combine some LiveData functions from my Dao and transform them into a single entity. I want to create something like this:
private val combinedValues(ld1, ld2, ld3, ld4){
first, second, third, fourth -> CombinedLiveDataValues(first, second, third, fourth)
}
val combinedEntity: LiveData<Any> = Transformations.map(combinedValues){ it->
val something = it.first.map (etc...)
}
How to create a way of not duplicating code and generalising and adding dozens of liveDatas together?
You can use something like this
fun<T> combine(context: AppCompatActivity, vararg input: LiveData<T>): LiveData<T> {
val output = MutableLiveData<T>()
input.forEach {
it.observe(context, androidx.lifecycle.Observer { value ->
output.value = value
})
}
return output
}

How to typesafe reduce a Collection of Either to only Right

Maybe a stupid question but I just don't get it.
I have a Set<Either<Failure, Success>> and want to output a Set<Success> with Arrow-kt.
You can map the set like this for right:
val successes = originalSet.mapNotNull { it.orNull() }.toSet()
or if you want the lefts:
val failures = originalSet.mapNotNull { it.swap().orNull() }.toSet()
The final toSet() is optional if you want to keep it as a Set as mapNotNull is an extension function on Iterable and always returns a List
PS: No stupid questions :)
Update:
It can be done avoiding nullables:
val successes = originalSet
.map { it.toOption() }
.filter { it is Some }
.toSet()
We could potentially add Iterable<Option<A>>.filterSome and Iterable<Either<A, B>.mapAsOptions functions.
Update 2:
That last example returns a Set<Option<Success>>. If you want to unwrap the results without using null then one thing you can try is to fold the Set:
val successes = originalSet
.fold(emptySet<Success>()) { acc, item ->
item.fold({ acc }, { acc + it })
}
This last option (unintended pun) doesn't require the use of Option.

Kotlin: function to stop iteration when predicate satisfied

I'm looking for a function in Kotlin which stops an iteration once a predicate is fulfilled:
val services = listOf(service1, service2)
...
var res: Result = null
services.stopIfPredicateFulFilled { service ->
res = service.doSomething()
res != null
}
While this example is not really nice since res is overwritten in each iteration I hope the intention is clear.
forEach doesn't do the job the way I expect it to be done. So, I was wondering if there isn't anything else.
You can use the functions find { ... } and firstOrNull { ... } (they are equivalent, just named differently). They find the first element satisfying the predicate and return that element, ignoring all the remaining elements.
services.find { service ->
res = service.doSomething()
res != null
}