How to collect 2 coroutine Flow<T>? - kotlin-coroutines

I have 2 SharedFlow<T>s and I want to take actions on their latest emissions. There is a combine operator which has the similar effect but I do not think it is supposed to be used that way since it is not a terminal operator. Please help!
val flow1 = MutableSharedFlow<Int>()
val flow2 = MutableSharedFlow<Double>()
flow1.collect(flow2) { a, b ->
//do something with a and b
}

You still need to collect, the combined flows, like:
val flow1 = MutableSharedFlow<Int>()
val flow2 = MutableSharedFlow<Double>()
flow1.combine(flow2) { a, b ->
//do something with a and b
a to b // for ex
}.collect{
}
See combine for more information

Related

Spring Data JPA filtering by best match

I want to implement filtering for multiple fields of an entity which is ordered by best match. By best match I mean that the more of the filtered fields match the higher in the order the result is listed. I want this to work dynamically, so I can add more filters later on.
I have been looking for a solution for a long time now and I didn't find an elegant way to do this with JPA.
My approach is to concatenate all my predicates with or and then order them by how many of the fields match. This is done by dynamically creating a CASE statement for each possible combination of the filters (this is a powerset and leads to a lot of CASE statements). Then I give every subset a rank (= size of the subset) and then I sort by the rank in descending order. This way subsets with more elements (= filters) are ranked higher.
From a few tests I can see that I already takes up to 10s for 4 filters, so that can't be a good solution.
Here is my code:
private fun orderByBestMatch(): Specification<User?> {
return Specification<User?> { root: Root<User?>, query: CriteriaQuery<*>, builder: CriteriaBuilder ->
val benefit = getExpressionForNestedClass<String>(root, "benefit")
val umbrellaTerm = getExpressionForNestedClass<String>(root, "umbrellaTerm")
val specialization = getExpressionForNestedClass<String>(root, "specialization")
val salaryExpectation = root.get<Number>("salaryExpectation")
val matcher: CriteriaBuilder.Case<Int> = builder.selectCase()
for (set in powerSetOfsearchedFields()) {
if(set.isNotEmpty()) {
var predicate: Predicate? = when(set.elementAt(0).key) {
"umbrellaTerm" -> builder.like(umbrellaTerm, set.elementAt(0).value.toString())
"specialization" -> builder.like(specialization, set.elementAt(0).value.toString())
"benefit" -> builder.like(benefit, set.elementAt(0).value.toString())
"salaryExpectation" -> builder.equal(salaryExpectation, set.elementAt(0).value.toString())
else -> null
}
for (i in 1 until set.size) {
predicate = when(set.elementAt(1).key) {
"umbrellaTerm" -> builder.and(predicate, builder.like(umbrellaTerm, set.elementAt(1).value.toString()))
"specialization" -> builder.and(predicate, builder.like(specialization, set.elementAt(1).value.toString()))
"benefit" -> builder.and(predicate, builder.like(benefit, set.elementAt(1).value.toString()))
"salaryExpectation" -> builder.and(predicate, builder.equal(salaryExpectation, set.elementAt(1).value.toString()))
else -> null
}
}
matcher.`when`(predicate, set.size)
}
}
matcher.otherwise(0)
query.orderBy(builder.desc(matcher))
query.distinct(true)
builder.isTrue(builder.literal(true))// just here for the function to have a return value
// result?.toPredicate(root, query, builder)
}
}
This function is used in a Builder class I implemented and is appended to the Specification with an and when building the Specification.
The Specification is then passed to UserRepository.findall().
Is there a better way (maybe even an out of the box way) to implement this behaviour?
Thanks in advance

Flux returns unsorted data for StepVerifier

I expect to receive two elements after writing three to the database. The query returns a Flux, which returns the elements unsorted.
#Test
fun `SUCESSFULLY query pending purchases`() {
// arrange
val arrived = TestDataFactory.buchungssatzEntity(
sequentialId = 1,
arrived = true
)
val pending = TestDataFactory.buchungssatzEntity(
sequentialId = 2,
arrived = false
)
val next_pending = TestDataFactory.buchungssatzEntity(
sequentialId = 3,
arrived = false
)
// act
buchhaltungWriter.save(arrived)
buchhaltungWriter.save(pending)
buchhaltungWriter.save(next_pending)
val purchases = inventoryFinder.findAllPendingPurchases()
// assert
StepVerifier.create(purchases)
.expectNext(pending)
.expectNext(next_pending)
.verifyComplete()
}
This always throws
expected value: BuchungssatzEntity(... sequentialId=2 ...); actual value: BuchungssatzEntity(... sequentialId=3 ...)
I do not want to add sorting to the database query, it's a waste of time. How can I test Flux with 'all of these elements but in any order' or should I just go for a Mono in this case, return a list and do the rest myself? Then again, that's not why I'm using reactive.
PS: Goal of this test is to verify that my Spring query language statement filters correctly.
-------- UPDATE
I solved it by using
StepVerifier.create(inventoryFinder.findAllPendingPurchases())
.recordWith { listOf<PurchasesModel>() }
.thenConsumeWhile { i: Any? -> purchases.contains(i) }
.consumeRecordedWith{ }
.verifyComplete()
One way to do that is to use the thenConsumeWhile operator along with expectNextCount. If there is any element in the sequence that doesn't match, the StepVerifier will error.
In Java:
List<Item> pendingItems = List.of(pending, next_pending);
Flux<Item> items = inventoryFinder.findAllPendingPurchases();
StepVerifier.create(items)
.expectNextCount(2) //expecting 2 elements
.verifyComplete();
StepVerifier.create(items)
.thenConsumeWhile((i) -> pendingItems.contains(i)) // check if element was expected
.verifyComplete();

IN operator in Couchbase Lite

I need to query something like
SELECT * FROM `sample` WHERE id IN ["123", "456"]
This is converted into QueryBuilder as below
QueryBuilder
.select(SelectResult.all())
.from(DataSource.database("sample"))
.where(Expression.property("id")
.in([
Expression.string("123"),
Expression.string("456")
])
)
This doesn't work, and return empty list of result, any idea?
I see from your comments that your code worked (it was false negative). Yet I'll share an approach which may be useful in case you need to use the like operator against multiple wildcarded (%) expressions. Sample is in Kotlin language:
val list = listOf("123", "456")
val items = list.map { Meta.id.like(Expression.string("$it-%")) }.toMutableList()
val whereExpression = items
.fold(items.removeAt(0)) { chain, next -> chain.or(next) }
val query = QueryBuilder.select(
SelectResult.expression(Meta.id),
SelectResult.all()
).from(DataSource.database(this.db)).where(whereExpression)
val results = query.execute()
val resultList = results.allResults()

Fast Ways To Update Value in Map

I having map structure like this:
val map = mutable.Map[String, Double].empty
Than I add value to my map like this
map("apple") = 10.34
But for next value of apple I want to add to 10.34 so I doing this
val oldVal = map("apple")
map("apple) = oldVal + 2.34
Is there more faster way I can do this? Because I have to read big file and I want fast update on map. Thank you for your advices.
val map = mutable.Map.empty[String, Double].withDefaultValue(0.0)
//put new
map("apple") = 10.34
//update existing
map("apple") += 2.34
//update not existing
map("orange") += 0.34
When using Scala it's generally better to avoid using mutable objects. This is to avoid any issues with concurrency which is fairly simple in Scala.
If I understood your question correctly you have a map that you want to update with values. When reading from a file you could create a new map with values to add:
val m = Map("a" -> 1, "b" -> 2)
val other = Map("a" -> 3, "c" -> 4) // created from a file
Now you can update the first map with values from the second map to get this:
val updated = m.map{ case (k, v) => { if(other.contains(k)) {(k, v + other.get(k).get)} else (k,v) }}
Now you can use updated to perform other operations with.

how to convert forEach to lambda

Iterator<Rate> rateIt = rates.iterator();
int lastRateOBP = 0;
while (rateIt.hasNext())
{
Rate rate = rateIt.next();
int currentOBP = rate.getPersonCount();
if (currentOBP == lastRateOBP)
{
rateIt.remove();
continue;
}
lastRateOBP = currentOBP;
}
how can i use above code convert to lambda by stream of java 8? such as list.stream().filter().....but i need to operation list.
The simplest solution is
Set<Integer> seen = new HashSet<>();
rates.removeIf(rate -> !seen.add(rate.getPersonCount()));
it utilizes the fact that Set.add will return false if the value is already in the Set, i.e. has been already encountered. Since these are the elements you want to remove, all you have to do is negating it.
If keeping an arbitrary Rate instance for each group with the same person count is sufficient, there is no sorting needed for this solution.
Like with your original Iterator-based solution, it relies on the mutability of your original Collection.
If you really want distinct and sorted as you say in your comments, than it is as simple as :
TreeSet<Rate> sorted = rates.stream()
.collect(Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(Rate::getPersonCount))));
But notice that in your example with an iterator you are not removing duplicates, but only duplicates that are continuous (I've exemplified that in the comment to your question).
EDIT
It seems that you want distinct by a Function; or in simpler words you want distinct elements by personCount, but in case of a clash you want to take the max pos.
Such a thing is not yet available in jdk. But it might be, see this.
Since you want them sorted and distinct by key, we can emulate that with:
Collection<Rate> sorted = rates.stream()
.collect(Collectors.toMap(Rate::getPersonCount,
Function.identity(),
(left, right) -> {
return left.getLos() > right.getLos() ? left : right;
},
TreeMap::new))
.values();
System.out.println(sorted);
On the other hand if you absolutely need to return a TreeSet to actually denote that this are unique elements and sorted:
TreeSet<Rate> sorted = rates.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(Rate::getPersonCount,
Function.identity(),
(left, right) -> {
return left.getLos() > right.getLos() ? left : right;
},
TreeMap::new),
map -> {
TreeSet<Rate> set = new TreeSet<>(Comparator.comparing(Rate::getPersonCount));
set.addAll(map.values());
return set;
}));
This should work if your Rate type has natural ordering (i.e. implements Comparable):
List<Rate> l = rates.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
If not, use a lambda as a custom comparator:
List<Rate> l = rates.stream()
.distinct()
.sorted( (r1,r2) -> ...some code to compare two rates... )
.collect(Collectors.toList());
It may be possible to remove the call to sorted if you just need to remove duplicates.

Resources