FlatMap an iterable and combine it back into iterable - java

I have an API that returns a Single. This Single contains a list of values, let's say String values. When I am calling this object, I get that Single and have to filter some values from it and return back another Single. I'm trying to achieve something like in this simplified test:
#Test
public void filterTest() {
List<String> sourceList = Arrays.asList("email", "phone", "smoke", "email", "phone", "fax", "email");
Single.just(sourceList)
.toObservable()
.flatMap(source -> {
return Observable.from(source);
})
.filter(source -> !source.equals("email"))
.groupBy(/* criteria? */)
//how to extract single list from groupBy or
//is there another opposite function for flatMap?
.toSingle()
.subscribe(s -> System.out.println(s));
}

Try this:
Single.just(sourceList)
.flattenAsObservable(source -> source)
.filter(source -> !source.equals("email"))
.toList()
.subscribe(s -> System.out.println(s));
or
Observable.fromIterable(sourceList)
.filter(source -> !source.equals("email"))
.toList()
.subscribe(s -> System.out.println(s));

Related

Convert List<Mono<String>> to Flux<String>

There are few question , but there answers are very specific to some code.
Generally, how to convert a Stream of Mono to Flux
List<Mono<String> listOfMono = stream()
.map( s -> { do something and return Mono<String> } )
.collect(Collectors.toList());
How to convert listOfMono object to Flux<String>
You can use fromIterable and then use flatMap to flatten the Mono
Transform the elements emitted by this Flux asynchronously into Publishers, then flatten these inner publishers into a single Flux through merging, which allow them to interleave.
Flux<String> result = Flux.fromIterable(listOfMono)
.flatMap(Function.identity());
If your input is a list of Monos you can simply do:
Flux.merge(listOfMono);
If your input is stream you can either do
stream()
.map( s -> { do something and return Mono<String> } )
.collect(Collectors.collectingAndThen(Collectors.toList(), Flux::merge));
OR
Flux.fromStream(stream())
.flatMap( s -> { do something and return Mono<String> } )
I'd personally prefer the last option as that is the most straighforward and idiomatic.

Java Lambda for break inside two for loops

I am trying to convert an iterative block of code in Java 8 to functional. The functional approach is unable to find the matching message in the set shared.
List<Optional<Message>> allMessages = new ArrayList<>();
Set<Status> allStatuses = getAllStatuses();
//Iterative : Working
Set<StatusMessage> set = new HashSet<>(STATUS_MESSAGE.values());
for (StatusMessage statusMessage : set) {
for (Status status : statusMessage.getStatusAndInfo().keySet()) {
Optional<Message> message = MessageBuilder.createMessage(allStatuses, status, this::createMessage);
if (message.isPresent()) {
allMessages.add(message);
break;
}
}
}
//Functional : Not working - Never adds anything to the
//map even when matching status is present
STATUS_MESSAGE.values().stream()
.distinct()
.map(statusMessage -> statusMessage.getStatusAndInfo().keySet())
.flatMap(Collection::stream)
.map(key -> MessageBuilder.createMessage(allStatuses, key, this::createMessage))
.anyMatch(allMessages::add);
The MessageBuilder.createMessage looks like this:
Optional<Status> matchingStatus = statuses.stream()
.filter(matchingStatus::equals)
.findFirst();
System.out.println("Found : " + matchingStatus.toString());
return matchingStatus.flatMap(creator);
Also, for debugging purposes, how can I see what is happening at each step of the stream? The stack in the debugger in intellij wasn't showing anything in the stream.
This should do it:
STATUS_MESSAGE.values().stream()
.distinct()
.forEach(statusMessage ->
statusMessage.getStatusAndInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.findFirst()
.ifPresent(allMessages::add)
);
UPDATE
To build the result list using toList instead of adding to a list:
List<Optional<Message>> allMessages = STATUS_MESSAGE.values().stream()
.distinct()
.flatMap(statusMessage ->
statusMessage.getStatusAndInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.limit(1)
)
.collect(Collectors.toList());
This should be a comment, but it's too long...
Seems like your MessageBuilder.createMessage method is overcomplicated.
Check below a simplified and more readable version of the same logic:
if (allStatuses.contains(status)) {
System.out.println("Found : " + status.toString());
return creator.apply(status);
}
return Optional.empty();
You should not use forEach for accumulating operations, so this should be more idiomatic:
Function<StatusInfo, Optional<Message>> messageForStatus = statusInfo ->
statusInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());
allMessages = STATUS_MESSAGE.values().stream()
.distinct()
.map(StatusMessage::getStatusAndInfo)
.map(messageForStatus)
.filter(Optional::isPresent)
.collect(toList());
As a side note, you have too many optionals, you may want to consider unwrapping some earlier, as a list of optionals may just as well be the list of only the present values.

RXJava persist return value of previous flatMap

In RX JAVA(java8), how can I persist value of previous flatMap or map.
public void createAccount(]) {
JsonObject payload = routingContext.getBodyAsJson();
socialService.getOAuthToken(payload)
.flatMap(token -> {
return getAllAccounts(token);
})
.flatMap(accounts -> {
// Save accounts with TOKENS
})
.subscribe(accountID -> {
response(accountID);
);
}
So in above code, in second flatMap how can I get the token from previous flatMap.
You have to zip account and token and pass it to the next Stream operation.
//Note you have to replace T, A with the right type
socialService.getOAuthToken(payload).flatMap(token -> getAllAccounts(token)
.map(account -> new SimpleImmutableEntry<T, A>(token, account)))
.flatMap(accounts -> /* accounts.getKey() -> token, accounts.getValue() -> account */)
.subscribe(accountId -> response(accountId));
Kotlin solution based on the solution by #Flown:
socialService.getOAuthToken(payload)
.flatMap { token ->
getAllAccounts(token)
.map { account -> Pair(token, account) }
}
.flatMap { (token, account) -> /* Use values here */ }
.subscribe { accountId -> response(accountId) }

can we improve this code?

I need to pick the first result based on 3 checks which need to be executed in sequence. i.e if there is no object that meets criterion 1, then we look for any object meeting criterion 2 and so on. Here is my working code
MyClass result = myObjects.stream()
.filter(s -> s.meetsCriterion1())
.findFirst()
.orElseGet(() -> {
return myObjects.stream()
.filter(s -> s.meetsCriterion2())
.findFirst()
.orElseGet(() -> {
return myObjects.stream()
.filter(s -> s.meetsCriterion3())
.findFirst()
.orElseGet(() -> {
return null;
});
});
});
can we improve this code? I am not sure if there is a way to reuse the first stream to evaluate all the criterion.
I would separate the list of criteria from the logic:
List<Predicate<MyClass>> criteria = Arrays.asList(
MyCass::meetsCriterion1,
MyCass::meetsCriterion2,
MyCass::meetsCriterion3
);
MyClass result = criteria.stream()
.flatMap(c -> myObjects.stream().filter(c).limit(1))
.findFirst()
.orElse(null); // questionable. consider redesigning to avoid null.

In RxJava, how to pass a variable along when chaining observables?

I am chaining async operations using RxJava, and I'd like to pass some variable downstream:
Observable
.from(modifications)
.flatmap( (data1) -> { return op1(data1); })
...
.flatmap( (data2) -> {
// How to access data1 here ?
return op2(data2);
})
It seems like a common pattern but I couldn't find information about it.
The advice I got from the Couchbase forum is to use nested observables:
Observable
.from(modifications)
.flatmap( (data1) -> {
return op1(data1)
...
.flatmap( (data2) -> {
// I can access data1 here
return op2(data2);
})
});
EDIT: I'll mark this as the accepted answer as it seems to be the most recommended. If your processing is too complex to nest everything you can also check the solution with function calls.
Another possibility is to map the result of op1 to a org.apache.commons.lang3.tuple.Pair that contains the variable and pass that along:
Observable
.from(modifications)
.flatmap( (data1) -> {
return op1(data1).map( obj -> { return Pair.of(data1,obj); });
})
...
.flatmap( (dataPair) -> {
// data1 is dataPair.getLeft()
return op2(dataPair.getRight());
})
It works but it feels a bit uncomfortable to have variables hidden inside a Pair/Triple/... and it gets very verbose if you use the Java 6 notation.
I wonder if there is a better solution, maybe some RxJava operator could help?
flatmap can take a second arg:
Observable.just("foo")
.flatMap(foo -> Observable.range(1, 5), Pair::of)
.subscribe(pair -> System.out.println("Result: " + pair.getFirst() + " Foo: " + pair.getSecond()));
source: https://medium.com/rxjava-tidbits/rxjava-tidbits-1-use-flatmap-and-retain-original-source-value-4ec6a2de52d4
One possibility would be to use a function call:
private static Observable<T> myFunc(final Object data1) {
return op1(data1)
...
.flatmap( (data2) -> {
// I can access data1 here
return op2(data2);
});
}
Observable
.from(modifications)
.flatmap( (data1) -> { return myFunc(data1); })
BUT: correct me if I'm wrong but it doesn't feel like the reactive-programming way of doing it
Actually we have library, that simplify call chains.
https://github.com/pakoito/Komprehensions
Adding as Gradle dependency:
implementation 'io.reactivex.rxjava2:rxjava:2.2.1'
implementation 'com.github.pakoito.Komprehensions:komprehensions-rx2:1.3.2'
Usage (Kotlin):
val observable = doFlatMap(
{ Observable.from(modifications) },
{ data1 -> op1(data1) },
{ data1, data2 -> op2(data2) },
{ data1, data2, data3 -> op3(data1, data2, data3) }
)
I know this is an old question, but using RxJava2 & lambda,
You can use something like:
Observable
.from(modifications)
.flatMap((Function<Data1, ObservableSource<Data2>>) data1 -> {
//Get data 2 obeservable
return Observable.just(new Data2())
}
}, Pair::of)
On the next flow (flatmap/map) your output pair will be (data1, data2)
solution on this thread works, but for complex chains it makes code difficult to read, I had to pass multiple values and what i did was create a private class with all parameters, I find code to be more readable this way,
private class CommonData{
private string data1;
private string data2;
*getters and setters*
}
...
final CommonData data = new CommonData();
Observable
.from(modifications)
.flatmap( (data1) -> {
data.setData1(data1);
return op1(data1);
})
...
.flatmap( (data2) -> {
data2 = data.getData1() + "data 2... ";
data.setData2(data2);
return op2(data2);
})
hope it helps
you can use resultSelector BiFunction<? super T, ? super U, ? extends R> resultSelector the second parameter in flatmap, you can choose which result to return.
You can use "global" variable to achive this:
Object[] data1Wrapper = new Object[]{null};
Object[] data2Wrapper = new Object[]{null};
Observable
.from(modifications)
.flatmap(data1 -> {
data1Wrapper[0] = data1;
return op1(data1)
})
...
.flatmap(data2 -> {
// I can access data1 here use data1Wrapper[0]
Object data1 = data1Wrapper[0];
data2Wrapper[0] = data2;
return op2(data2);
})

Categories