I have the following logic in my method where I check for the value of an optional parameter, and depending on that I build another object.
AtomicReference<Employee> employeeValue = null;
questions.forEach(question -> {
if(question.isBoolean().isPresent()) {
employeeValue.set(Employee.builder()
.withBooleanValue(Boolean.valueOf(question.value()))
.build());
} else {
employeeValue.set(Employee.builder()
.withStringValue(question.value())
.build());
}
Record record = Record.builder()
.withId(question.id())
.withValue(employeeValue.get())
.build();
answers.add(record);
});
How can I replace the above with ifPresent and orElse? I'm using Java 8 and therefore ifPresentOrElse method is not available. If I am to use ifPresent and orElse separately with anonymous inner function, how do I go about it?
Any help would be much appreciated.
You neither need isPresent() nor ifPresent(). You don’t need peek() (as in the other answer) nor an AtomicReference (as in the question). I believe that this does it:
questions.forEach(question -> {
Employee empl = question.isBoolean()
.map(b -> Employee.builder()
.withBooleanValue(Boolean.valueOf(question.value()))
.build())
.orElseGet(() -> Employee.builder()
.withStringValue(question.value())
.build());
Record record = Record.builder()
.withId(question.id())
.withValue(empl)
.build();
answers.add(record);
});
You can probably apply this idea inside the stream from the other answer if you want. Rather than using Stream.forEach() I’d prefer to collect into a collection like a list and then use answers.addAll().
You can stream through questions and use peek and map-orElse construction to achieve the same result:
questions.stream()
.peek(question -> {
Employee employee = question.isBoolean()
.map(b -> Employee.builder().withBooleanValue(Boolean.valueOf(question.value())).build())
.orElse(Employee.builder().withStringValue(question.value()).build());
employeeValue.set(employee);
}
)
.map(question -> Record.builder().withId(question.id()).withValue(employeeValue.get()).build())
.forEach(answers.add(answer)); // did you mean 'record'?
But to be honest it does not change a lot - your implementation looks maybe less "java eightish" but is fine :)
Related
Presently I am doing this
final ReactiveHashOperations<String, String, String> ops = redisTemplate.opsForHash();
var theMonoIWant =
Mono.fromCallable(this::generateSomeComplexDataThatProducesTheValueIWant)
.flatMap(
theValueIWant -> {
... there is something here ...
return ops.putAll(somewhere, something)
.filter(success -> success)
.switchIfEmpty(
Mono.error(IllegalStateException::new)
)
.thenReturn(theValueIWant);
}
);
I also tried this and it worked, but I am thinking it may be a fluke it did, because I don't know how long it will actually take to complete the subscription and there may be a race condition for it.
final ReactiveHashOperations<String, String, String> ops = redisTemplate.opsForHash();
var theMonoIWant =
Mono.fromCallable(this::generateSomeComplexDataThatProducesTheValueIWant)
.doOnNext(
theValueIWant -> {
ops.putAll(somewhere, something)
.filter(success -> success)
.switchIfEmpty(
Mono.error(IllegalStateException::new)
)
.subscribe();
}
);
But what I am looking for is something like
var theMonoIWant =
Mono.fromCallable(this::generateSomeComplexDataThatProducesTheValueIWant)
.doSubscriptionOnNextButIgnoreTheReturnValueUnlessItsAnError(
theValueIWant ->
ops.putAll(somewhere, something)
.filter(success -> success)
.switchIfEmpty(
Mono.error(IllegalStateException::new)
);
);
Nope, the first solution is the idiomatic one.
"doSubscriptionOnNext" is quite literally flatMap.
Ignoring the value(s) from said inner subscription is something better described in the inner publisher via composition rather than trying to predict all possible patterns as top-level flatMap variants.
ignoreElements() or in your case thenReturn are good ways of achieving that in the flatmapping function.
I have a soa xml java object, in which I need to get the total count of SItemDetails which is 4 here:
<ns57:PProductsResponse xmlns:ns57="">
<ResponseInfo xmlns="" TransactionId="test"/>
<ns57:PProductsSuccess>
<ONumber xmlns="" ONumber="7">
<OItemNumber>
<PItemDetails Price="0.00" >
<SItemDetails FIdRef="01-01" SId="12D"/>
<SItemDetails FIdRef="01-02" SId="10F"/>
</PItemDetails>
</OItemNumber>
</ONumber>
</ns57:PProductsSuccess>
<ns57:PProductsSuccess>
<ONumber xmlns="" ONumber="7">
<OItemNumber>
<PItemDetails Price="0.00">
<SItemDetails FIdRef="01-02" SId="10G"/>
<SItemDetails FIdRef="01-01" SId="12E"/>
</PItemDetails>
</OItemNumber>
</ONumber>
</ns57:PProductsSuccess>
</ns57:PProductsResponse>
PProductsSuccessType[] pProductSuccess = pProductsResponse.getPProductsResponse().getPProductsSuccess();
long sItemDetailsCount1 = Arrays.stream(pProductSuccess).filter(PProductsSuccessType
-> (PProductsSuccessType.getONumber().getOItemNumber()[0].getPItemDetails().getSItemDetails()!=null)).count();
OR
long sItemDetailsCount2 = Arrays.stream(pProductSuccess)
.flatMap(p -> Arrays.stream(p.getONumber().getOItemNumber()))
.filter(o -> o.getPItemDetails().getSItemDetails() != null).count();
OR
long sItemDetailsCount3 = Arrays.stream(pProductSuccess)
.map(p -> p.getONumber().getOItemNumber())
.flatMap(Arrays::stream)
.filter(Objects::nonNull)
.count();
When I executed the above codes it gave the result as 2 but i am expecting 4 since we have 4 SItemDetails in the pProductsResponse.
Can someone help me achieve it using lamda iteration.
A lambda expression is just an anonymous function. It doesn't look like lambda's are your issue here. It looks like you're confused on what your streams are doing.
I'd suggest reading through https://www.baeldung.com/java-8-streams-introduction to get a better understanding of using the streams api.
Your IDE should already do this for you but each method chained together below is returning an object. Map and flatmap both return Stream<>. I've added some comments of what I can guess is being returned from your method calls.
long sItemDetailsCount3 = Arrays.stream(pProductSuccess) //Stream<pProductSuccess>
.map(p -> p.getONumber().getOItemNumber()) //Stream<List<OItemNumber>>
.flatMap(Arrays::stream) // Stream<OItemNumber>
.filter(Objects::nonNull) // Stream<OItemNumber>
.count(); //The number of items in the Stream<OItemNumber>
As you can see, your count would be returning the count of OItemNumber's since that's what the stream is iterating over.
To get the SItemDetails we need to have a stream of SItemDetails. This should work for your needs. I am using flatmap since I assume your getters are returning a Collection<> of items. In short, flatmap will flatten the collection and give a Stream while map would give
Stream<List<item>>
Arrays.stream(pProductSuccess) //Stream<pProductSuccess>
.flatmap(p -> p.getONumber().getOItemNumber().stream()) //Stream<OItemDetails>
.flatmap(oItemNumber -> oItemNumber.getPItemDetails().stream())//Stream<PItemDetails>
.flatmap(pItemDetail -> pItemDetail.getSItemDetails().stream())//Stream<SItemDetails>
.filter(Objects::nonNull) // Stream<SItemDetails>
.count();
If you are ever confused about what is being returned from a stream, remove the method chaining and do something similar to
Stream<OItemNumber> oItemNumberStream = Arrays.stream(pProductSuccess)
.flatmap(p -> p.getONumber().getOItemNumber().stream())
Stream<> anotherStream = oItemNumberStream.flatmap(....);
By assigning local variables it is easier to see what object is returned from each call. Your IDE should help do this by displaying what would be returned on each line as long as you format your code so a method call is each on it's own line (as shown in your examples above).
I have this code
public Stream<PaymentEntity> getPayments(List<String> paymentIds) {
return paymentIds.stream()
.flatMap(id -> paymentsRepository.getById(id))
}
paymentsRepository.getById(id) is returning Single<PaymentEntity>
But i got compile time error
no instance(s) of type variable(s) R exist so that
Single conforms to Stream
Edit, to anyone asking about Single http://reactivex.io/documentation/single.html
You can use
return paymentIds.stream()
.map(id -> paymentsRepository.getById(id).toBlocking().value());
Or in rxjava 2 you can use :
return paymentIds.stream()
.map(id -> paymentsRepository.getById(id).blockingGet());
After comment I think you can go with this solution :
List<PaymentEntity> result = new ArrayList<>();
paymentIds.forEach(id -> paymentsRepository.getById(id).toObservable().subscribe(result::add));
return result.stream();
Alternate solution:
public Stream<PaymentEntity> getPayments(List<String> paymentIds) {
return Observable.fromIterable(paymentIds)
.flatMapSingle(id -> paymentsRepository.getById(id))
.toList()
.blockingGet()
.stream();
}
You can wait until the current Single in the lambda signals a success value using .blockingGet():
return paymentIds.stream()
.map(id -> paymentsRepository.getById(id).blockingGet());
Or you can wait until all the reactive chain signals a success value in this way:
return Observable.fromIterable(paymentIds)
.flatMapSingle(paymentsRepository::getById)
.toList()
.blockingGet()
.stream();
Consider also to use Flowable: unlike Observable, it supports backpressure strategy.
I am new in Java8 and I want to refactor this piece of code and convert it in a more Java8 style,
for (RestaurantAddressee RestaurantAddressee : consultationRestaurant.getAddressees()) {
Chain chain = chainRestService.getClient().getChainDetails(getTDKUser(), RestaurantAddressee.getChain().getId());
if (chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId())) {
chainIds.add(restaurantAddressee.getChain().getId());
}
}
so I change it for this code:
consultationRestaurant.getAddressees()
.stream()
.map( ma -> chainRestService.getClient().getChainDetails(getTDKUser(), ma.getChain().getId()))
.filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
.forEach(chainIds.add(chain.getId()));
But I have this compilation error:
chain cannot be resolved
You forgot to specify the lambda expression parameter in your forEach call.
That said, you shouldn't use forEach to add elements to a collection. Use collect:
List<String> chainIds =
consultationRestaurant.getAddressees()
.stream()
.map( ma -> chainRestService.getClient().getChainDetails(getTDKUser(), ma.getChain().getId()))
.filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
.map(Chain::getId)
.collect(Collectors.toList());
Here. Your loop defines:
Chain chain = chainRestService.getClient()...
But your stream statement simply misses to define that variable.
So: in places that need that variable, you have to provide, for example as parameter:
filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
Using a Java 8 lambda expression, I'm trying to do something like this.
List<NewObject> objs = ...;
for (OldObject oldObj : oldObjects) {
NewObject obj = oldObj.toNewObject();
obj.setOrange(true);
objs.add(obj);
}
I wrote this code.
oldObjects.stream()
.map(old -> old.toNewObject())
.forEach({new.setOrange("true")})
.collect(Collectors.toList());
This is invalid code because I'm then trying to do .collect() on what's returned by .forEach(), but forEach is void and does not return a list.
How should this be structured?
You can use Stream's peek method, which returns the Stream because it's an intermediate operation. It normally isn't supposed to have a side effect (it's supposed to be "non-interfering"), but in this case, I think the side effect (setOrange(true)) is intended and is fine.
List<NewObject> newObjects =
oldObjects.stream()
.map(OldObject::toNewObject)
.peek( n -> n.setOrange(true))
.collect(Collectors.toList());
It's about as verbose as your non-streams code, so you can choose which technique to use.
You can use peek.
List<NewObject> list = oldObjects.stream()
.map(OldObject::toNewObject)
.peek(o -> o.setOrange(true))
.collect(Collectors.toList());
Alternatively, you can mutate the elements after forming the list.
List<NewObject> list = oldObjects.stream()
.map(OldObject::toNewObject)
.collect(Collectors.toList());
list.forEach(o -> o.setOrange(true));