How can I merge both values using Single.flatMap? - java

I have this code:
private Single<Invoice> getInvoiceWithItems() {
return getInvoice().flatMap(invoice -> getItems(invoice)); // <--- Here, I need invoice and items
}
private Single<Invoice> getInvoice() { ... }
private Single<List<Item>> getItems(Invoice invoice) { ... }
I want to do something like invoice.setItems(items). I tried passing an extra function parameter to flatMap but it doesn't accept it.
How can I do it?
I found this solution, but I'm not sure if it is the best one:
private Single<Invoice> getInvoiceWithItems() {
return Single.zip(getInvoice(), getInvoice().flatMap(invoice -> getInvoiceItems(invoice)), (invoice, items) -> {
invoice.setItems(items);
return invoice;
});
}

private Single<Invoice> getInvoiceWithItems() {
return getInvoice().flatMap(invoice -> getItems(invoice).map(items -> {
invoice.setItems(items);
return invoice;
}));
}

Related

How to use streams to return boolean value depending of enum type - SpringBoot

I have a method with which I would like to check if, in my model, enum of some type exists.
If it exists I want to return true an if not I would like to return false with error message.
I have model that looks like this:
#Data
public class DataResponse {
public final String userId;
public final List<Model> modelList;
}
Model.java
#Data
public class Model {
public final String modelId;
public final ModelType type;
public final ModelStatus status;
}
ModelType.java
public enum ModelType {
Fast,
Slow;
}
ModelStatus.java
public enum ModelStatus {
CREATED,
FAILED
}
Now I would like to check using strems if my model contains type "Fast" and if contains status "CREATED" if yes return true if not return false with error message.
So far I have this:
public Boolean isModelFastAndSuccess(String modelId){
Optional<DataResponse> modelList = dataService.getModelStatus(modelId);
userProofList.stream().map(ml -> ml.modelList)
.map(models -> {
if()
})
.collect(Collectors.toList());
}
I am not sure how to finish it.
Any advice appreciated.
The approach to implementation of isModelFastAndSuccess could be as follows assuming that dataService::getModelStatus returns Optional<DataResponse>:
public Boolean areAllModelsFastAndSuccess(String modelId) {
return dataService.getModelStatus(modelId) // Optional<DataResponse>
.map(DataResponse::getModelList) // Optional<List<Model>>
.orElse(Collections.emptyList()) // List<Model>
.stream() // Stream<Model>
.allMatch(model -> model.getType() == ModelType.Fast
&& model.getStatus() == ModelStatus.CREATED
);
}
Also, a method may be implemented to return true if there is at least one Fast / Created model:
public Boolean isAnyModelFastAndSuccess(String modelId) {
return dataService.getModelStatus(modelId) // Optional<DataResponse>
.map(DataResponse::getModelList) // Optional<List<Model>>
.orElse(Collections.emptyList()) // List<Model>
.stream() // Stream<Model>
.anyMatch(model -> model.getType() == ModelType.Fast
&& model.getStatus() == ModelStatus.CREATED
);
}

Convert sequential Monos to Flux

I have a web service where I want to retrieve the elements of a tree up to the root node.
I have a Webflux interface which returns a Mono on each call:
public interface WebService {
Mono<Node> fetchNode(String nodeId);
}
public class Node {
public String id;
public String parentId; // null, if parent node
}
Let's assume there is a tree
1
2 3
4 5
I want to create the following method:
public interface ParentNodeResolver {
Flux<Node> getNodeChain(String nodeId);
}
which would give me on getNodeChain(5) a Flux with the nodes for 5, 3 and 1 and then completes.
Unfortunately, I don't quite understand how I can combine Monos sequentially, but without blocking them. With Flux.generate(), I think I need to block on each mono to check whether it has a next element. Other methods which I've found seem to combine only a fixed number of Monos, but not in this recursive fashion.
Here is a sample code which would simulate the network request with some delay.
public class MonoChaining {
ExecutorService executorService = Executors.newFixedThreadPool(5);
#Test
void name() {
var nodeChain = generateNodeChainFlux("5")
.collectList()
.block();
assertThat(nodeChain).isNotEmpty();
}
private Flux<Node> generateNodeChainFlux(String nodeId) {
//TODO
return Flux.empty();
}
public Mono<Node> getSingleNode(String nodeId) {
var future =
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // Simulate delay
if ("5".equals(nodeId)) {
return new Node("5", "3");
} else if ("3".equals(nodeId)) {
return new Node("3", "1");
} else if ("1".equals(nodeId)) {
return new Node("1", null);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}, executorService);
return Mono.fromFuture(future);
}
public static class Node {
public String id;
public String parentId;
public Node(String id, String parentId) {
this.id = id;
this.parentId = parentId;
}
}
}
Is there a way to retrieve this?
Thanks!
The operator you are looking for is Mono#expand. It is used for recursively expanding sequences. Read more here.
In your case:
private Flux<Node> generateNodeChainFlux(String nodeId) {
return getSingleNode(nodeId).expand(node -> getSingleNode(node.parentId));
}
Using recursion with flatMap to get parent node and concat to append the current node to resulting flux might work. Try the code below:
public Flux<Node> getNodeChain(String nodeId) {
return fetchNode(nodeId).flatMapMany(node -> {
if (node.parent != null) {
Flux<Node> nodeChain = getNodeChain(node.parent);
return Flux.concat(Flux.just(node), nodeChain);
}
return Flux.just(node);
});
}
Here I'm using flatMapMany to convert Mono to Flux.

RxJava - How to get Single from nested method

In my Presenter i have a method which gets some list from DataHolder:
disposable.add(dataHolder.getMonthOfAttractions(monthInAdvance)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<Map<String, List<Attraction>>>() {
#Override
public void onSuccess(Map<String, List<Attraction>> stringListMap) {
}
#Override
public void onError(Throwable e) {
}
}));
Then, in my DataHolder I'm checking if my list isn't null. If true, returns my list, if false it downloads this list from server :
public Single<Map<String, List<Attraction>>> getMonthOfAttractions(int monthInAdvance) {
Map<String, List<Attraction>> monthOfAttractions = monthlyAttractionsMap.get(monthInAdvance);
if (monthOfAttractions != null)
return Single.fromCallable(() -> monthOfAttractions);
else
return apiGetMonthOfAttractions(monthInAdvance);
The problem is with apiGetMonthOfAttractions method. I dont know how to correctly implement this method to return value to my Presenter.
I've tried something like:
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
.subscribeWith(new CnkApiObserver<AttractionListResponse>() {
#Override
public void onSucceeded(AttractionListResponse result) {
result.getMonthOfAttractions();
}
#Override
public void onFailed(Error error) {
}
});
}
But in this case i have "missing return statement" and I'm out of ideas how to implement it. I'm begging to learn RxJava, so be understanding.
Please help :)
EDIT:
This is what how my Retrofit getAttractions() method looks like:
public interface CnkApiInterface {
#GET("pl/front-api/{dateFrom}/{dateTo}")
Single<AttractionListResponse> getAttractions(#Path("dateFrom") String dateFrom, #Path("dateTo") String dateTo);}
This is what you are after:
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface()
.getAttractions(monthInAdvance)
.flatMap(attractionListResponse -> Single.just(attractionListResponse.getMonthOfAttractions()));
}
I thing just try to do something like (it only depends what does your cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance) returns)
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
}
should do the trick
You can always just map the result to List<Attraction> so #wojech_maciejewski s answer still holds, you jast need to add a mapping function.
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
.map(atractions -> /* convert to List<Attraction> here */)
}

Best way to iterate two lists and extract few things?

I have two classes as shown below. I need to use these two classes to extract few things.
public final class ProcessMetadata {
private final String clientId;
private final String deviceId;
// .. lot of other fields here
// getters here
}
public final class ProcMetadata {
private final String deviceId;
private final Schema schema;
// .. lot of other fields here
}
Now I have below code where I am iterating above two classes and extracting schema given a clientId.
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
}
}
return Optional.absent();
}
Is there any better way of getting what I need by iterating those two above classes in couple of lines instead of what I have? I am using Java 7.
You're doing a quadratic* search operation, which is inneficient. You can do this operation in constant time by first creating (in linear time) a mapping from id->object for each list. This would look something like this:
// do this once, in the constructor or wherever you create these lists
// even better discard the lists and use the mappings everywhere
Map<String, ProcessMetadata> processMetadataByClientId = new HashMap<>();
for (ProcessMetadata process : processMetadataList) {
processMetadataByClientId.put(process.getClientId(), process);
}
Map<String, ProcMetadata> procMetadataByDeviceId = new HashMap<>();
for (ProcMetadata metadata2 : procMetadataList) {
procMetadataByDeviceId.put(proc.getDeviceId(), proc);
}
Then your lookup simply becomes:
public Optional<Schema> getSchema(String clientId) {
ProcessMetadata process = processMetadataByClientId.get(clientId);
if (process != null) {
ProcMetadata proc = procMetadataByDeviceId.get(process.getDeviceId());
if (proc != null) {
return Optional.of(proc.getSchema());
}
}
return Optional.absent();
}
In Java 8 you could write it like this:
public Optional<Schema> getSchema(String clientId) {
return Optional.fromNullable(processMetadataByClientId.get(clientId))
.map(p -> procMetadataByDeviceId.get(p.getDeviceId()))
.map(p -> p.getSchema());
}
* In practice your algorithm is linear assuming client IDs are unique, but it's still technically O(n^2) because you potentially touch every element of the proc list for every element of the process list. A slight tweak to your algorithm can guarentee linear time (again assuming unique IDs):
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
// adding a break here ensures the search doesn't become quadratic
break;
}
}
return Optional.absent();
}
Though of course using maps ensures constant-time, which is far better.
I wondered what could be done with Guava, and accidentally wrote this hot mess.
import static com.google.common.collect.Iterables.tryFind
public Optional<Schema> getSchema(final String clientId) {
Optional<String> deviceId = findDeviceIdByClientId(clientId);
return deviceId.isPresent() ? findSchemaByDeviceId(deviceId.get()) : Optional.absent();
}
public Optional<String> findDeviceIdByClientId(String clientId) {
return tryFind(processMetadataList, new ClientIdPredicate(clientId))
.transform(new Function<ProcessMetadata, String>() {
String apply(ProcessMetadata processMetadata) {
return processMetadata.getDeviceId();
}
});
}
public Optional<Schema> findSchemaByDeviceId(String deviceId) {
return tryFind(procMetadataList, new DeviceIdPredicate(deviceId.get())
.transform(new Function<ProcMetadata, Schema>() {
Schema apply(ProcMetadata procMetadata) {
return processMetadata.getSchema();
}
});
}
class DeviceIdPredicate implements Predicate<ProcMetadata> {
private String deviceId;
public DeviceIdPredicate(String deviceId) {
this.deviceId = deviceId;
}
#Override
public boolean apply(ProcMetadata metadata2) {
return metadata2.getDeviceId().equalsIgnoreCase(deviceId)
}
}
class ClientIdPredicate implements Predicate<ProcessMetadata> {
private String clientId;
public ClientIdPredicate(String clientId) {
this.clientId = clientId;
}
#Override
public boolean apply(ProcessMetadata metadata1) {
return metadata1.getClientId().equalsIgnoreCase(clientId);
}
}
Sorry.

How to execute logic on Optional if not present?

I want to replace the following code using java8 Optional:
public Obj getObjectFromDB() {
Obj obj = dao.find();
if (obj != null) {
obj.setAvailable(true);
} else {
logger.fatal("Object not available");
}
return obj;
}
The following pseudocode does not work as there is no orElseRun method, but anyways it illustrates my purpose:
public Optional<Obj> getObjectFromDB() {
Optional<Obj> obj = dao.find();
return obj.ifPresent(obj.setAvailable(true)).orElseRun(logger.fatal("Object not available"));
}
With Java 9 or higher, ifPresentOrElse is most likely what you want:
Optional<> opt = dao.find();
opt.ifPresentOrElse(obj -> obj.setAvailable(true),
() -> logger.error("…"));
Currying using vavr or alike might get even neater code, but I haven't tried yet.
I don't think you can do it in a single statement. Better do:
if (!obj.isPresent()) {
logger.fatal(...);
} else {
obj.get().setAvailable(true);
}
return obj;
For Java 8 Spring Data offers ifPresentOrElse from "Utility methods to work with Optionals" to achieve what you want.
Example would be:
import static org.springframework.data.util.Optionals.ifPresentOrElse;
ifPresentOrElse(dao.find(), obj -> obj.setAvailable(true), () -> logger.fatal("Object not available"));
You will have to split this into multiple statements. Here is one way to do that:
if (!obj.isPresent()) {
logger.fatal("Object not available");
}
obj.ifPresent(o -> o.setAvailable(true));
return obj;
Another way (possibly over-engineered) is to use map:
if (!obj.isPresent()) {
logger.fatal("Object not available");
}
return obj.map(o -> {o.setAvailable(true); return o;});
If obj.setAvailable conveniently returns obj, then you can simply the second example to:
if (!obj.isPresent()) {
logger.fatal("Object not available");
}
return obj.map(o -> o.setAvailable(true));
There is an .orElseRun method, but it is called .orElseGet.
The main problem with your pseudocode is that .isPresent doesn't return an Optional<>. But .map returns an Optional<> which has the orElseGet method.
If you really want to do this in one statement this is possible:
public Optional<Obj> getObjectFromDB() {
return dao.find()
.map( obj -> {
obj.setAvailable(true);
return Optional.of(obj);
})
.orElseGet( () -> {
logger.fatal("Object not available");
return Optional.empty();
});
}
But this is even clunkier than what you had before.
First of all, your dao.find() should either return an Optional<Obj> or you will have to create one.
e.g.
Optional<Obj> = dao.find();
or you can do it yourself like:
Optional<Obj> = Optional.ofNullable(dao.find());
this one will return Optional<Obj> if present or Optional.empty() if not present.
So now let's get to the solution,
public Obj getObjectFromDB() {
return Optional.ofNullable(dao.find()).flatMap(ob -> {
ob.setAvailable(true);
return Optional.of(ob);
}).orElseGet(() -> {
logger.fatal("Object not available");
return null;
});
}
This is the one liner you're looking for :)
For those of you who want to execute a side-effect only if an optional is absent
i.e. an equivalent of ifAbsent() or ifNotPresent() here is a slight modification to the great answers already provided.
myOptional.ifPresentOrElse(x -> {}, () -> {
// logic goes here
})
Title: "How to execute logic on Optional if not present?"
Answer:
Use orElseGet() as a workaround for the missing ifNotPresent(). And since it expects us to return something just return
null.
Optional.empty().orElseGet(() -> {
System.out.println("The object is not present");
return null;
});
//output: The object is not present
or
Optional.ofNullable(null).orElseGet(() -> {
System.out.println("The object is not present");
return null;
});
//output: The object is not present
I also use it to easily implement the singleton pattern with lazy initialization.
public class Settings {
private Settings(){}
private static Settings instance;
public static synchronized Settings getInstance(){
Optional.ofNullable(instance).orElseGet(() -> instance = new Settings());
return instance;
}
}
Of course the getInstance() content can be written in one line by directly returning the first statement, but I wanted to demonstrate the use of orElseGet() as an ifNotPresent().
I was able to came up with a couple of "one line" solutions, for example:
obj.map(o -> (Runnable) () -> o.setAvailable(true))
.orElse(() -> logger.fatal("Object not available"))
.run();
or
obj.map(o -> (Consumer<Object>) c -> o.setAvailable(true))
.orElse(o -> logger.fatal("Object not available"))
.accept(null);
or
obj.map(o -> (Supplier<Object>) () -> {
o.setAvailable(true);
return null;
}).orElse(() () -> {
logger.fatal("Object not available")
return null;
}).get();
It doesn't look very nice, something like orElseRun would be much better, but I think that option with Runnable is acceptable if you really want one line solution.
With Java 8 Optional it can be done with:
Optional<Obj> obj = dao.find();
obj.map(obj.setAvailable(true)).orElseGet(() -> {
logger.fatal("Object not available");
return null;
});
In order to get the value from one call, or do an extra call if the previous returned an empty value, you can chain the commands.
public Optional<Obj> getObjectFromDB() {
return dao.find().or(() -> dao.findBySomethingElse());
}
You need Optional.isPresent() and orElse(). Your snippet won;t work because it doesn't return anything if not present.
The point of Optional is to return it from the method.
ifPresentOrElse can handle cases of nullpointers as well. Easy approach.
Optional.ofNullable(null)
.ifPresentOrElse(name -> System.out.println("my name is "+ name),
()->System.out.println("no name or was a null pointer"));
I suppose you cannot change the dao.find() method to return an instance of Optional<Obj>, so you have to create the appropriate one yourself.
The following code should help you out. I've create the class OptionalAction,
which provides the if-else mechanism for you.
public class OptionalTest
{
public static Optional<DbObject> getObjectFromDb()
{
// doa.find()
DbObject v = find();
// create appropriate Optional
Optional<DbObject> object = Optional.ofNullable(v);
// #formatter:off
OptionalAction.
ifPresent(object)
.then(o -> o.setAvailable(true))
.elseDo(o -> System.out.println("Fatal! Object not available!"));
// #formatter:on
return object;
}
public static void main(String[] args)
{
Optional<DbObject> object = getObjectFromDb();
if (object.isPresent())
System.out.println(object.get());
else
System.out.println("There is no object!");
}
// find may return null
public static DbObject find()
{
return (Math.random() > 0.5) ? null : new DbObject();
}
static class DbObject
{
private boolean available = false;
public boolean isAvailable()
{
return available;
}
public void setAvailable(boolean available)
{
this.available = available;
}
#Override
public String toString()
{
return "DbObject [available=" + available + "]";
}
}
static class OptionalAction
{
public static <T> IfAction<T> ifPresent(Optional<T> optional)
{
return new IfAction<>(optional);
}
private static class IfAction<T>
{
private final Optional<T> optional;
public IfAction(Optional<T> optional)
{
this.optional = optional;
}
public ElseAction<T> then(Consumer<? super T> consumer)
{
if (optional.isPresent())
consumer.accept(optional.get());
return new ElseAction<>(optional);
}
}
private static class ElseAction<T>
{
private final Optional<T> optional;
public ElseAction(Optional<T> optional)
{
this.optional = optional;
}
public void elseDo(Consumer<? super T> consumer)
{
if (!optional.isPresent())
consumer.accept(null);
}
}
}
}

Categories

Resources