So, I have sorted by condition list of objects
private Observable<CallServiceCode> getUnansweredQuestionList() {
return Observable.fromIterable(getServiceCodeArrayList())
.subscribeOn(Schedulers.computation())
.filter(iServiceCode -> iServiceCode.getServiceCodeFormStatus().isUnanswered());
}
and now what I need to do:
Every object has list servicePartList , I need to filter this list by condition and eventually if final size of this filtered list >0, so I need to add object that contains this list CallServiceCode object as a key and this filtered list as a value.
So it should be like this:
private Map<CallServiceCode, ArrayList<CallServicePart>> getSortedMap() {
Map<CallServiceCode, ArrayList<CallServicePart>> result = new HashMap<>();
getUnansweredQuestionList()
.filter(callServiceCode -> Observable.fromIterable(callServiceCode.getCallServicePartList()) //
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered())//
.isNotEmpty())
.subscribe(callServiceCode -> result.put(callServiceCode, Observable.fromIterable(callServiceCode.getCallServicePartList()) //
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered()));
return result;
}
But there is no such method isNotEmpty() in RxJava2 and also it is not right to add key like this:
Observable.fromIterable(callServiceCode.getCallServicePartList())
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered())
So question is how to make it properly?
One solution could be to use collect to create the Map directly from the observable:
return getUnansweredQuestionList()
.collect(HashMap<CallServiceCode, List<CallServicePart>>::new,(hashMap, callServiceCode) -> {
List<CallServicePart> callServiceParts = Observable.fromIterable(callServiceCode.getServicePartList())
.filter(s -> !s.getServicePartFormStatus().isUnanswered())
.toList().blockingGet();
if (!callServiceParts.isEmpty())
hashMap.put(callServiceCode, callServiceParts);
}).blockingGet();
If you extract filtering into a method (could be also member of CallServiceCode) then the code is much cleaner:
return getUnansweredQuestionList()
.collect(HashMap<CallServiceCode, List<CallServicePart>>::new, (hashMap, callServiceCode) -> {
List<CallServicePart> filteredParts = getFilteredServiceParts(callServiceCode.getServicePartList());
if (!filteredParts .isEmpty())
hashMap.put(callServiceCode, filteredParts);
}).blockingGet();
Related
Hope You're all doing will. I'm very new to this and I keep getting issues with incompatible types.
Flowable<Boolean> checkTriggerDaily() {
List<Bson> fields = new ArrayList<Bson>();
fields.add(exists("dueDate", true));
Bson filter = and(fields);
Flowable.fromPublisher(marketplaceMongoService.getCollection().find(filter)).map{ third ->
getTheReport(third.id).flatMap { size ->
TaskService.createTasks(size).toFlowable().flatMap({})
}
}
}
I keep getting the same error on the title. What this function does is loop through a mongo collection and call getTheReport every item. What the getTheReportReturns, i process it to createTasks function.
getTheReport -> returns Maybe
createTasks -> returns Maybe
Since
getTheReport(third.id).flatMap { size ->
TaskService.createTasks(size).toFlowable().flatMap({})
}
result is a Flowalbe, you should call flatMap instead of map so that the internal function emitted objects get unpacked into a flat result:
Flowable<Boolean> checkTriggerDaily() {
List<Bson> fields = new ArrayList<Bson>();
fields.add(exists("dueDate", true));
Bson filter = and (fields);
Flowable.fromPublisher(marketplaceMongoService.getCollection().find(filter))
.flatMap { third -> // flatMap instead of map
getTheReport(third.id).flatMap { size ->
TaskService.createTasks(size).toFlowable().flatMap({})
}
}
}
I have the following stream that works without any problem:
final List<ProductCategoryDTO> productCategoryList = productCategoryService
.findAllByMenu(menuUuid)
.stream()
.filter(category -> !category.getProductList().isEmpty())
.collect(Collectors.toList());
I just want to add another filter:
final List<ProductCategoryDTO> productCategoryList = productCategoryService
.findAllByMenu(menuUuid)
.stream()
.filter(category -> !category.getProductList().isEmpty())
.flatMap(product -> product.getProductList().stream())
.filter(menu -> !menu.getMenuItemProperties().isDisabled())
.collect(Collectors.toList());
But it throws required type is List<ProductCategoryDTO> and returned type is List<MenuItemCategoryDTO>. I also tried to use a single filter by adding && condition to the original filter, but cannot perform this.
.filter(category -> !category.getProductList().isEmpty()
&& category.getProductList().stream()
.filter(p -> !p.getMenuItemProperties().isDisabled()))
So, what is the correct usage for this situation? There is a product list in productCategoryList and I need to filter based on menuItemProperties().isDisabled() property. Any idea?
Update:
public class ProductCategoryDTO {
private UUID uuid;
private UUID menuUuid;
private List<MenuItemCategoryDTO> productList;
}
You might try it like this using mapMulti (introduced in Java 16)
stream the categories
for each Product (if any exist)
iterate across the properties for that product
continue iterating until at least one is enabled
then accept that one which adds the product to the stream.
then return a list
List<ProductCategoryDTO> productCategoryList = productCategoryService
.findAllByMenu(menuUuid)
.stream()
.<ProductCategoryDTO>mapMulti((category, consumer)-> {
for(Product prod : getProductList()) {
if (prod.getMenuItemsProperties().isDisabled()) {
// check next product
continue;
}
// at least one enabled one found so accept category
consumer.accept(category);
break;
}
}).collect(Collectors.toList());
If you want to ensure that only categories are accepted where all the properites are enabled, the you can try it like this.
List<ProductCategoryDTO> productCategoryList = productCategoryService
.findAllByMenu(menuUuid)
.stream()
.<ProductCategoryDTO>mapMulti((category, consumer)-> {
for(Product prod : getProductList()) {
if (prod.getMenuItemsProperties().isDisabled()) {
return;
}
}
// all are enabled so accept category
consumer.accept(category);
}).collect(Collectors.toList());
I am trying to learn how to use the lambda functions for sleeker code but struggling to make this work.
I have two lists. The "old" list is always shorter or the same length as the "updated list".
I want to take the objects from the "updated list" and overwrite the "stale objects" in the shorter "old list".
The lists have a unique field for each object.
For example, it is a bit like updating books in a library with new editions. The UUID (title+author) remains the same but the new object replaces the old on the shelf with a new book/object.
I know I could do it the "long way" and make a HashMap<MyUniqueFieldInMyObject, MyObject> and then take the new List<MyUpdatedObjects> and do the same.
I.e. Have HashMap<UniqueField, MyOldObject> and HashMap<UniqueField, MyUpdatedObject>, then iterate over the old objects with a pseudo "if updated objects have an entry with the same key, overwrite the value with the updated value"...
But...
Is there a "nicer" shorted way to do this with functional lambda statements?
I was thinking along the lines of:
List<MyObject> updatedList;
List<MyObject> oldList;
updatedList.forEach(MyObject -> {
String id = MyObject.getId();
if (oldList.stream().anyMatcher(MyObject ->
MyObject.getId().matches(id)) {
//Do the replacement here? If so...how?
}
}
Which is where I am lost!
Thanks for any guidance.
If you want to update the list in place rather than making a new list, you can use List.replaceAll:
oldList.replaceAll(old ->
updateListe.stream()
.filter(updated -> updated.getId().equals(old.getId())
.findFirst()
.orElse(old)
);
The main problem with this solution is that its complexity is O(size-of-old*size-of-updated). The approach you described as "long way" can protect you from having to iterate over the entire updated list for every entry in the old list:
// note that this will throw if there are multiple entries with the same id
Map<String, MyObject> updatedMap = updatedList.stream()
.collect(toMap(MyObject::getId, x->x));
oldList.replaceAll(old -> updatedMap.getOrDefault(old.getId(), old));
I recommend you to iterate over the oldList - the one you want to update. For each of the object iterated match the equivalent one by its id and replace it using Stream::map. If an object is not found, replace it with self (doesn't change the object) using Optional::orElse.
List<MyObject> newList = oldList
.stream() // Change values with map()
.map(old -> updatedList.stream() // Iterate each to find...
.filter(updated -> old.getId() == updated.getId()) // ...by the same id
.findFirst() // Get new one to replace
.orElse(old)) // Else keep the old one
.collect(Collectors.toList()); // Back to List
List<Foo> updatedList = List.of(new Foo(1L, "new name", "new desc."));
List<Foo> oldList = List.of(new Foo(1L, "old name", "old desc."));
List<Foo> collect = Stream.concat(updatedList.stream(), oldList.stream())
.collect(collectingAndThen(toMap(Foo::getId, identity(), Foo::merge),
map -> new ArrayList(map.values())));
System.out.println(collect);
This will print out:
[Foo{id=1, name='new name', details='old desc.'}]
In Foo::merge you can define which fields need update:
class Foo {
private Long id;
private String name;
private String details;
/*All args constructor*/
/*getters*/
public static Foo merge(Foo newFoo, Foo oldFoo) {
return new Foo(oldFoo.id, newFoo.name, oldFoo.details);
}
}
I think it's best to add the objects to be updated into a new list to avoid changing a list you are streaming on and then you can simply replace the old with the new list
private List<MyObject> update(List<MyObject> updatedList, List<MyObject> oldList) {
List<MyObject> newList = new ArrayList<>();
updatedList.forEach(object -> {
if (oldList.stream().anyMatch(old -> old.getUniqueId().equals(object.getUniqueId()))) {
newList.add(object);
}
}
return newList;
}
I have a case where A has a list of B and B has some property i need.
pseudocode structure
class A
List<B> elements;
class B
Property property;
I have List of A. In order, to get property i should go through the double foreach loop to get the property i want. Like this:
String myFutureProp = null;
for (A a: list of A) {
for(B b: list of B) {
if("MY_PROPERTY".equals(b.getKey) {
myFutureProp = b.getValue();
}
}
}
I was thinking to get it more tasty using Stream API. I was looking forward with
forEach() solution:
final String[] myFutureProp = {null}
a.getElements()
.foreach(b -> b.getElements().stream()
.filter("MY_PROPERTY"::equals)
.forEach(prop -> myFutureProp[0] = (String)prop.getValue);
Then i'm taking myFutureProp[0] but it looks ugly to be honest. Is it any another solution in Stream API that i'm able to use?
I hope I got your class structure right.
You can use flatMap to get a Stream of all the Propertys of all the B instances, and return any property having the required key (if found) :
Optional<String> myFutureProp =
aList.stream()
.flatMap(a->a.getElements().stream().map(B::getProperty))
.filter(prop->prop.getKey().equals("MY_PROPERTY"))
.map(Property::getValue)
.findAny();
This should do a work -> get last matching value
final String value = bList.stream()
.flatMap(bElement -> bElement.elements().stream())
.map(aElement -> aElement.getProperty())
.filter(property -> property.getKey().equals("MY_PROPERTY"))
.map(property -> property.getValue())
.reduce((a,b) -> b).orElse(null);
Is there any way to do it with java 8 Stream API?
I need to transform each item of collection to other type (dto mapping) and return all set as a list...
Something like
Collection<OriginObject> from = response.getContent();
DtoMapper dto = new DtoMapper();
List<DestObject> to = from.stream().forEach(item -> dto.map(item)).collect(Collectors.toList());
public class DtoMapper {
public DestObject map (OriginObject object) {
return //conversion;
}
}
Thank you in advance
Update #1: the only stream object is response.getContent()
I think you're after the following:
List<SomeObject> result = response.getContent()
.stream()
.map(dto::map)
.collect(Collectors.toList());
// do something with result if you need.
Note that forEach is a terminal operation. You should use it if you want to do something with each object (such as print it). If you want to continue the chain of calls, perhaps further filtering, or collecting into a list, you should use map.