I want to zip two monos/fluxes, but the second one (the one I'll zip) is dependand from the first that I already own.
For example:
//...
fun addGroup(
input: GroupInput
): Mono<Group> = Mono.just(Group(title = input.title, description = input.description))
.flatMap { g -> groupsRepository.save(g) } // Gives me back the new db ID
.zipWith(Mono.just(GroupMember(g.id /* <-- ??*/, input.ownerId, true)))
//...
// ...
Is it possible?
I would say no. You can only zip things that can run in parallel.
you could use map or flatmap in this case, is there any reason you need to use Zip?
No, your code cannot even compile because variable g belongs to the lambda inside the flatMap() call.
zipWith() is not intended for this use case, use map() or flatMap()
Related
Is there any way to use both FailableFunction and Function lambdas into a single .map chaining stream?
Function<String, Organization> hook = (id) -> this.organizationRepository.findById(id).get();
FailableFunction<Organization, Organization, MpiException> failableSave = (r) -> this.organizationRepository.save(r);
List<String> ids;
Failable.stream(ids)
.map(hook)
.map(failableSave)
.collect(Collectors.toList());
I'm getting:
The method map(FailableFunction<String,R,?>) in the type Streams.FailableStream is not applicable for the arguments (Function<String,Organization>)
Problem here is that sometimes I need to use Function and other times I need to use FailableFunction.
Any ideas about how to use them into same stream mapping chaining?
Maybe not the most elegant solution, but since nobody else came up with something:
You could use a method reference to implicitly convert a Function into a FailableFunction:
Failable.stream(ids)
.map(hook::apply)
.map(failableSave)
.collect(Collectors.toList());
new Vavr user here.
I am trying to pattern match a tuple of options to execute a statements if both of them are Some, in Scala I would have done this with:
val maybeThis: Option[String] = ???
val maybeThat: Option[String] = ???
(maybeThis, maybeThat) match {
case (Some(dis), Some(that)) => ???
case _ => ???
}
In Java, I am trying this approach:
Tuple2<Option<String>, Option<String>> tuple = new Tuple2<>(Option.of(...), Option.of(...));
return Match(tuple).of(
Case($Tuple2($Some($(instanceOf(String.class))), $Some($(instanceOf(String.class)))),
(someThis, someThat) -> methodWhichEatsTwoStrings(someThis.get(), someThat.get())),
Case($(), t -> run(() -> {
throw new NullPointerException(...);
})));
However, with this implementation the compiler complains that it was expecting Some<Object> instead of Some<String>, same error happens if I omit the $(instanceOf(String.class) in the pattern.
I am moderately sure this is merely a problem of proper syntax, yet I am struggling to find the correct documentation.
Where am I wrong?
Thanks
There's a vavr API construct called for comprehension that tries to mimic Scala's for comprehensions to the extent that it's possible to do in Java. With that construct you could solve your problem quite elegantly. See the For overload for handling two Options for more details. Here's an example code snippet:
String methodWhichEatsTwoStrings(String v1, String v2) {
return v1 + v2; //combine the two values in some way
}
...
Option<String> option1 = Option.some("value1");
Option<String> option2 = Option.some("value2");
String combined = For(option1, option2)
.yield((v1, v2) -> methodWhichEatsTwoStrings(v1, v2))
.getOrElseThrow(() -> new NullPointerException("reasons"));
Of course, you could use Option wrapping values of different types for option1 and option2, or combine multiple options, not just two. You could also use a different type for the return value of the yield function as well. I used String everywhere for the sake of simplicity and to conform to your original example.
I would like to add that I would try to avoid throwing NullPointerException in case one or both of the options are empty. Maybe try to use another vavr data type like Either to represent such an error case?
I have some concatenated observable using flatMap such as
api.call1()
.flatMap{a-> api.call2(a)}
.flatMap{b-> api.call3(a,b) //<- no access to a}
the issue is that In third concatenated call I need what I got from previous one but also the result from the first one
this is the scheme:
A->(a)->B->(b)-> C( needs b but also a)->...
I try to solve it out like that :
1 approach ( nested 3rd observable inside the 2nd one)
api.call1()
.flatMap{a-> api.call2(a).flatMap{b-> api.call3(a,b)}}
it works but I know that this is not a good approach (because of passing parms from outside of the pipe)
2 approach (better but a lot of boilerplate)
api.call1().flatMap{ a->
Observable.combineLatest(Observable.just(a),api.call2(a),
BiFunction{ a,b -> Pair(a,b)})
.flatMap(pair:Pair<A,B> -> api.call3(pair.first,pair.second))
}}
anybody know better approach (some fashion operator)
Thanks everybody sory for my poor ingles.
There is a second overload of flatMap which takes both the input and the output. You can then combine these into a Pair to send to the next flatMap
api.call1()
.flatMap(
{ a -> api.call2(a) },
{ a, b -> new Pair(a, b) }
)
.flatMap { pair-> api.call3(pair.first, pair.second) }
I have some data stored in a JPA Repository that I am trying to process. I would like to be able to use Java 8 Streams to do so, but can not figure out how to get the required information. This particular 'Entity' is actually only for recovery, so it holds items that would need to be processed after something like a power-fail/restart.
Using pre-Java 8 for-loops the code would look like:
List<MyEntity> deletes = myEntityJpaRepository.findByDeletes();
for (MyEntity item : deletes) {
String itemJson = item.getData();
// use a Jackson 'objectMapper' already setup to de-serialize
MyEventClass deleteEvent = objectMapper.readValue(itemJson, MyEventClass.class);
processDelete(deleteEvent, item.getId());
}
The problem arises from the two parameter method called at the very end. Using Streams, I believe I would do:
// deletes.stream()
// .map(i -> i.getData())
// .map(event -> objectMapper.readValue(event, MyEventClass.class))
// .forEach(??? can't get 'id' here to invoke 2 parameter method);
I have a solution (without Streams) that I can live with. However I would think this problem comes up a lot, thus my question is: IN GENERAL, is there a way using Streams to accomplish what I am trying to do?
Why not a Pair return on your map operation:
.map(i -> new Pair<>(i.getData(), i.getId()))
.map(pair -> new Pair<>(objectMapper.readValue(pair.getLeft(), MyEventClass.class), pair.getRight())
.forEach(p -> processDelete(pair.getLeft(), pair.getRight()))
I did not compile this, so there might be minor things to fix. But in general, you would need a Holder to pass your objects to the next stage in such a case. Either a Pair or some type or even a array.
Why not doing it simply this way?
deletes.forEach(item ->
processDelete(objectMapper.readValue(item.getData(), MyEventClass.class),
item.getId()));
This is a start at least, I guess it is dependent on why you want to use stream and how much you want to make it more functional
List<MyEntity> deletes = myEntityJpaRepository.findByDeletes();
deletes.stream().foreach(item -> {
String itemJson = item.getData();
// use a Jackson 'objectMapper' already setup to de-serialize
MyEventClass deleteEvent = objectMapper.readValue(itemJson, MyEventClass.class);
processDelete(deleteEvent, item.getId());
});
I am pretty new to streams.
I would like to stream the geometries EC_Geometry arraylist and if the EC_Geometry element is not present (or better equals never returns true), then I add it.
public void init(GL3 gl3, EC_Mesh mesh) {
geometries.stream()
.filter(geometry -> mesh.getGeometry().equals(geometry))
.findAny()
.orElse(..?);
}
But I am stuck at the last line
How can I solve it using streams?
Please note that equals is a method I wrote checking if the geometry is the same (i.e: if the triangles correspond)
orElse will always run even if the value returned isn't used so it is preferable to use orElseGet here which will only run if nothing is found.
geometries.stream()
.filter(geometry -> mesh.getGeometry().equals(geometry))
.findAny()
.orElseGet(() -> {
geometries.add(mesh.getGeometry());
return mesh.getGeometry();
});
.findAny().orElse(..?);
is for Optional - if you would like to get first element found.
For what you would like to achieve the best approach would be just to:
meshG = mesh.getGeometry();
if (!geometries.contains(meshG)) {
geometries.add(meshG);
}
No need to overuse Stream API.