Continue zip(), if one source is completed - spring-webflux

I have some troubles with .zip() operator.
Let me simplify my problem on a small example.
Flux<Integer> flux1 = Flux.just(9, 8, 3, -2);
Flux<Integer> flux2 = Flux.just(7);
Flux<Integer> flux3 = Flux.just(6, 5, 4, -4);
List<Flux<Integer>> list1 = Arrays.asList(flux1, flux2, flux3);
TreeSet<Integer> set = new TreeSet<>(Comparator.reverseOrder());
Set<Integer> list = Flux.zip(list1, objects -> {
boolean setChanged = false;
for (Object o : objects) {
Integer i = (Integer) o;
if (set.size() < 5 || i > set.last()) {
setChanged = true;
set.add(i);
if (set.size() > 5) {
set.pollLast();
}
}
}
return setChanged;
}).takeWhile(val -> val)
.then(Mono.just(set))
.block();
System.out.println(list);
Here I have 3 different sources(they are sorted descending by default, and the number of them could be much bigger), and I want to get from them a collection of 5 elements sorted descending. Unfortunately, I can't just use concat() or merge() operators, because sources in a real life can be really big, but I need only small amount of elements.
I am expecting [9, 8, 7, 6, 5] here, but one of the sources is completed after first iteration of zipping.
Could you please suggest how I can get around with this problem?

You can try the reduce operation
#Test
void test() {
Flux<Integer> flux1 = Flux.just(9, 8, 3, -2);
Flux<Integer> flux2 = Flux.just(7, 0, -2, 4,3,2,2,1);
Flux<Integer> flux3 = Flux.just(6, 5, 4, -4);
var k = 5;
List<Flux<Integer>> publishers = List.of(flux1, flux2, flux3);
var flux = Flux.merge(publishers)
.log()
.limitRate(2)
.buffer(2)
.reduce((l1, l2) -> {
System.out.println(l1);
System.out.println(l2);
return Stream.concat(
l1.stream(),
l2.stream()
).sorted(Comparator.reverseOrder())
.limit(k)
.collect(Collectors.toList());
})
.log();
StepVerifier.create(flux)
.expectNext(List.of(9,8,7,6,5))
.expectComplete()
.verify();
}
You can fetch data in chunks and compare them to find the top K elements.
In a sequential case it will fetch a new batch, compare it to the current top k result and return a new topk like in the example above (PriorityQueue may work better for sorting if k is big).
If you're using parallel schedulers and batches are fetched in parallel, then it can compare them with each other independently that should be a bit faster.
Also you have full control over the fetched data via rateLimit, buffer, delayElements, etc

Related

Create List<Object> from List<List<Object>> in kotlin

I have List<List> and I want to create List where I have all cars from List<List>. There is any way in Kotlin to create that (using map or something)? I don't want to create new list and add items in loops
val listOfLists: List<List<Int>> = listOf(listOf(1, 2, 3), listOf(4, 5))
One way would be to use flatten function. This function just flattens the inner lists
val flatten: List<Int> = listOfLists.flatten() //[1, 2, 3, 4, 5]
But if you need to do some transformation as well as flattening the list then you can do it with flatMap():
val flatMap: List<Int> = listOfLists.flatMap { it.map { it*2 } } //[2, 4, 6, 8, 10]
tl;dr List.flatten()
Let's say your List<List<Car>> is called carLists, then you can simply call val cars: List<Car> = carLists.flatten() if you want to have a single list containing all the cars from the list of lists of cars.
Code only:
val carLists: List<List<Car>> = … // created or received
val cars = carLists.flatten()

Having an issue with mutablemap in Kotlin

I'm working on an algorithm type challenge, and i am debugging via print statements and i can't seem to figure out why the the values for keys are not what i am expecting
var mapNums = mutableMapOf<Int, Int>()
//imaginary array
//var nums = [34,28,11,21,3,34,8,7,34,7,31,7,3,28,18]
var count = 0
for (n in nums) {
if (mapNums.containsKey(n)) {
count ++
mapNums[n] = count
} else if (!mapNums.containsKey(n)) {
count = 1
mapNums[n] = count
}
}
println(mapNums)
//prints {34=2, 28=4, 11=1, 21=1, 3=3, 8=1, 7=2, 31=1, 18=1}
as you can see the key and values aren't what theyre supposed to be and i am not sure why.
You can use the following code to generate the desired map:
val nums = intArrayOf(34, 28, 11, 21, 3, 34, 8, 7, 34, 7, 31, 7, 3, 28, 18).toList()
println(nums.groupingBy { it }.eachCount())
try it yourself
Here groupingBy creates a Grouping source using the same element as the key selector. Then eachCount groups elements from the Grouping source by key and counts elements in each group.
You can also refer the documentation for more info about groupingBy and eachCount.
It's because you reuse the same count variable outside of the loop so it keeps incrementing from different keys.
Instead you should get the current count from the map, then put it back one higher:
val nums = intArrayOf(34,28,11,21,3,34,8,7,34,7,31,7,3,28,18)
val mapNums = mutableMapOf<Int, Int>()
for (n in nums) {
val count = mapNums[n] ?: 0
mapNums[n] = count + 1
}
println(mapNums) // {34=3, 28=2, 11=1, 21=1, 3=2, 8=1, 7=3, 31=1, 18=1}
Firstly check n number is contain this map as key, if found then increment 1 its value using plus method. If not found any value from the map, it will null and check if null and set 1.
var mapNums = mutableMapOf<Int, Int>()
//imaginary array
var nums = arrayOf(34,28,11,21,3,34,8,7,34,7,31,7,3,28,18)
for (n in nums) {
mapNums[n] = mapNums[n]?.plus(1) ?: 1
}
println(mapNums)

How to get elements in one Flux that does not appear in another Flux

I am new to Spring Reactive Project. There was a problem in use.
I have two Flux, One has more elements, such as
Flux<Integer> bigFlux = Flux.range(1, 10);
And another likes
Flux<Integer> smallFlux = Flux.just(3, 7);
How can I get the elements in bigFlux that not appears in smallFlux?
I don't know which operator to use.
I have tried:
Flux<Integer> flux = bigFlux.filterWhen(one -> smallFlux.hasElement(one).map(a->!a));
But this is not wise, I got smallFlux through complex operations, such as querying the database, flatMap operations. In this way, how many elements in bigFlux, how many times these operations will be repeated.
In fact, smallFlux is obtained in this way.
Flux<File> usedFile = repository.findAll()
.flatMap(one -> {
List<File> used = someMethods(one);
return Flux.fromIterable(used);
});
Are there other better solutions, thanks.
I think this will be a cleaner and faster solution
final Flux<Integer> bigListFlux = Flux.just(1, 2, 3);
final Flux<Integer> smallListFlux = Flux.just(3, 5, 6);
Mono.zip(bigListFlux.collectList(), smallListFlux.collectList(), (bigList, smallList) -> {
bigList.removeAll(smallList);
return bigList;
}).flatMapMany(Flux::fromIterable).map(element -> {
System.out.println("element = " + element);
return element;
}).subscribe(); // do not use subscribe/block in actual production code.
I've used the following variant of Mono.zip
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#zip-reactor.core.publisher.Mono-reactor.core.publisher.Mono-java.util.function.BiFunction-

How to map a list by chunks in kotlin

I often end up with data sources like (pseudo code below, not any specific syntax, it is just to illustrate):
list = {
"XLabel",
"XDescription",
"YLabel",
"YDescription",
"ZLabel",
"ZDescription"
}
desired output is:
list = {
MyClass("XLabel", "XDescription"),
MyClass("YLabel", "YDescription"),
MyClass("ZLabel", "ZDescription")
}
Is there anything more clean than to do a fold(), and fold it into a new list? I've also rejected doing something weird like list.partition().zip()
I basically want a more powerfull map that would work like mapChunks( it1, it2 -> MyClass(it1, it2)) where the chunking is part of the function so it gets easy and nice. (My example has the list in chunks of two, but 3 is also a prevalent use case.)
Does this function exist? Or what is the most idiomatic way to do this?
You can use the chunked function, and then map over the result. The syntax gets very close to what you wanted if you destructure the lambda-argument:
list.chunked(2)
.map { (it1, it2) -> MyClass(it1, it2) }
// Or use _it_ directly: .map { MyClass(it[0], it[1]) }
I think the windowed method should do what you want.
lst.windowed(size = 2, step = 2, partialWindows = false) { innerList -> MyClass(innerList[0], innerList[1]) }
You can also use chunked but it calls windowed under the hood. But with chunked you can get lists that have fewer elements than you were expecting
EDIT to answer #android developer's question about getting the indexes of the list
val lst = listOf(7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
val windowedList = lst.mapIndexed { index, it -> index to it }
.windowed(size = 2, step = 2, partialWindows = false) {
it[0].first
}
println(windowedList)
Would output
[0, 2, 4, 6, 8]
To add to the existing answers, you can use chunked function with the transform lambda passed as its second argument:
list.chunked(2) { (label, description) -> MyClass(label, description) }
This way is more efficient because the temporary list of two elements is reused across all chunks.
You can create an extension function, for example mapChunks, and reuse it:
fun List<String>.mapChunks(): List<MyClass> {
return chunked(2).map { MyClass(it[0], it[1]) }
}
val list1 = listOf(
"XLabel",
"XDescription",
"YLabel",
"YDescription",
"ZLabel",
"ZDescription"
)
val result1 = list1.mapChunks()
val list2 = listOf(
"XLabel1",
"XDescription1",
"YLabel1",
"YDescription1",
"ZLabel1",
"ZDescription1"
)
val result2 = list2.mapChunks()
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html
Chunked returns a sub list of the size you specify
This is the API call you want
considering your list is in pairs of 2 you can do this
list.chunked(2) //List<List<String>>
.map{MyClass(it[0], it[1]} //list<MyClass>

What is the most optimized way to get a set of rows which is present in the middle of the list in java 8?

I've a list of items. I want to process a set of items which are in the middle of the list.
Ex: Assume a list of employees who have id, first name, last name and middle name as attributes.
I want to consider all rows between lastName "xxx" and "yyy" and process them further.
How can this be optimized in Java8? Optimization is my first concern.
Tried using Java8 streams and parallel streams. But termination(break) is not allowed in foreach loop in Java8 streams. Also we cannot use the outside("start" variable below) variables inside foreach.
Below is the code which I need to optimize:
boolean start = false;
for(Employee employee: employees) {
if(employee.getLastname().equals("yyy")) {
break;
}
if(start) {
// My code to process
}
if(employee.getLastname().equals("xxx")) {
start = true;
}
}
What is the best way to handle the above problem in Java8?
That is possible in java-9 via (I've simplified your example):
Stream.of(1, 2, 3, 4, 5, 6)
.dropWhile(x -> x != 2)
.takeWhile(x -> x != 6)
.skip(1)
.forEach(System.out::println);
This will get the values in the range 2 - 6, that is it will print 3,4,5.
Or for your example:
employees.stream()
.dropWhile(e -> e.getLastname().equals("xxx"))
.takeWhile(e -> e.getLastname().equals("yyy"))
.skip(1)
.forEach(....)
There are back-ports for dropWhile and takeWhile, see here and here
EDIT
Or you can get the indexes of those delimiters first and than do a subList (but this assumes that xxx and yyy are unique in the list of employees):
int[] indexes = IntStream.range(0, employees.size())
.filter(x -> list.get(x).getLastname().equals("xxx") || list.get(x).getLastname().equals("yyy"))
.toArray();
employees.subList(indexes[0] + 1, indexes[1])
.forEach(System.out::println);