Java 8 stream: parse list of maps - java

I am trying to get list of Long from list of maps using Stream API.
This is how class looks like:
class Report Bean{
private MarshallableMap data;
}
Object data contains records like this: ("ID", 1), ("Name", "TestName").
I need to get list of IDs from list of ReportBeans.
This is what I've tried:
List<Long> ids = reportBeans.stream().flatMap(
m -> m.getData().entrySet().stream()
.filter(e -> e.getKey().equals("ID"))
.map(Map.Entry::getValue)
.map(Long.class::cast))
.collect(Collectors.toList());
I am getting empty list. Please, I need advice. Thank you

There is a lot of unnecessary streaming going on. I think this is more suited for your needs:
reportBeans.stream()
.map(r -> r.getData().get("ID"))
.filter(Objects::nonNull)
.map(Long.class:cast)
.collect(toList());

You have not shown how your class 'MarshallableMap' looks like. It is necessary for us to provide an exact answer. But if your 'data' field is of type Map<String, String>, you can get the list of IDs the following way:
List<Long> ids = reportBeans.stream()
.flatMap(
m -> (m.getData().entrySet().stream()
.filter(e -> "ID".equals(e.getKey()))
.map(e -> Long.valueOf(e.getValue())))
)
.collect(Collectors.toList());

Related

Filter inner list using stream in Java

I want to apply a second filter for my product category list as shown below:
final List<ProductCategoryDTO> productCategoryList = productCategoryService
.findAllByUuid(uuid)
.stream()
.filter(category -> !category.getProductList().isEmpty())
// I am trying to filter the productCategoryList based on
// the "isDisabled" value of inner list >>>
.filter(category -> category.getProductList().stream()
.anyMatch(p -> !p.getMenuProperties().isDisabled()))
.collect(Collectors.toList());
The first filter !category.getProductList().isEmpty() is applied to the productCategoryList (outer list) and then I also want to apply a filter based on inner list's isDisabled value. I tried to use flatMap and concatenate the filter conditions as shown below, but none of them is not working:
.filter(category -> !category.getProductList().isEmpty() &&
!category.getProductList().stream()
.anyMatch(p -> p.getMenuProperties().isDisabled()))
So, how can I filter productCategoryList based on inner list value?
Here are the related DTO's:
public class ProductCategoryDTO {
private UUID uuid;
private List<MenuCategoryDTO> productList;
}
public class MenuCategoryDTO {
private MenuPropertiesDTO menuProperties;
}
Update: I also need to retrieve the list of UUID values of products by flattening the lists as shown below. But it does not work:
final List<UUID> productUuidList = productCategoryList.stream()
.flatMap(a -> a.getProductList().stream())
.flatMap(b -> b.getMenuProperties().getUuid())
.collect(Collectors.toList());
Any idea?
If your category.getProductList() is not an immutable list, then you could use
List<ProductCategoryDTO> list = productCategoryService.findAllByUuid(uuid);
list.stream()
.filter(category -> !category.getProductList().isEmpty())
.forEach(category -> category.getProductList()
.removeIf(p -> p.getMenuProperties().isDisabled()));
You get Stream of MenuCategoryDTO object after 1st flatMap. So you don't need 2nd flatMap. You can use Map instead.
final List<UUID> productUuidList = productCategoryList.stream()
.flatMap(a -> a.getProductList().stream())
.map(b -> b.getMenuProperties().getUuid())
.collect(Collectors.toList());

Project reactor, Convert Flux into Map<String,List<Data>>

I have a list of Data object
public class Data{
private String id;
private String sharedId;
}
How to convert this list into Map<String,List<Data>>, collecting the data object sharing the same sharedId together
I have tried to use this line of code, but no luck
Map<String,List<Data>> dataMap= Flux.fromIterable(list)
.collectMap(item-> item.getSharedId(),
item-> item);
Stream API is not equal to Reactive API and they are not interchangeable.
Use Stream API for grouping into Map<String, List<Data>> from List<Data>:
Map<String,List<Data>> dataMap = list.stream()
.collect(Collectors.groupingBy(Data::getSharedId));
Using Reactive API to achieve you achieve Flux<GroupedFlux<String, Data>> analogically or expand the structure using Map.Entry:
Flux<GroupedFlux<String, Data>> groupedFlux = Flux.fromIterable(list)
.groupBy(Data::getSharedId);
Flux<Map.Entry<String, List<Data>>> groupedFlux = Flux.fromIterable(list)
.groupBy(Data::getSharedId)
.flatMap(group -> group
.collectList()
.map(listOfData -> Map.entry(group.key(), listOfData)));
The following should work:
Map<String,List<Data>> dataMap =
list.stream().collect(groupingBy(Data::getSharedId));
With regards to previous answers from Nikolas & Joao you could also use:
Mono<Map<String, Collection<Data>>> mapCollection = Flux.fromIterable(list)
.collectMultimap(Data::getSharedId, item -> item);
you can map further to specific collection if it needs to be a list:
Mono<Map<String, List<Data>>> mapList = mapCollection
.map(map -> map
.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getKey, e -> List.copyOf(e.getValue()))))
or use:
Mono<Map<String, List<Data>>> mapList2 = Mono.just(list)
.map(l -> l.stream().collect(Collectors.groupingBy(Data::getSharedId)));

Lambda & Stream : collect in a Map

I would like to build a Map using the Stream & Lambda couple.
I've tried many ways but I'm stucked. Here's the classic Java code to do it using both Stream/Lambda and classic loops.
Map<Entity, List<Funder>> initMap = new HashMap<>();
List<Entity> entities = pprsToBeApproved.stream()
.map(fr -> fr.getBuyerIdentification().getBuyer().getEntity())
.distinct()
.collect(Collectors.toList());
for(Entity entity : entities) {
List<Funder> funders = pprsToBeApproved.stream()
.filter(fr -> fr.getBuyerIdentification().getBuyer().getEntity().equals(entity))
.map(fr -> fr.getDocuments().get(0).getFunder())
.distinct()
.collect(Collectors.toList());
initMap.put(entity, funders);
}
As you can see, I only know how to collect in a list, but I just can't do the same with a map. That's why I have to stream my list again to build a second list to, finally, put all together in a map.
I've also tried the 'collect.groupingBy' statement as it should too produce a map, but I failed.
It seems you want to map whatever is on the pprsToBeApproved list to your Funder instances, grouping them by buyer Entity.
You can do it as follows:
Map<Entity, List<Funder>> initMap = pprsToBeApproved.stream()
.collect(Collectors.groupingBy(
fr -> fr.getBuyerIdentification().getBuyer().getEntity(), // group by this
Collectors.mapping(
fr -> fr.getDocuments().get(0).getFunder(), // mapping each element to this
Collectors.toList()))); // and putting them in a list
If you don't want duplicate funders for a particular entity, you can collect to a map of sets instead:
Map<Entity, Set<Funder>> initMap = pprsToBeApproved.stream()
.collect(Collectors.groupingBy(
fr -> fr.getBuyerIdentification().getBuyer().getEntity(),
Collectors.mapping(
fr -> fr.getDocuments().get(0).getFunder(),
Collectors.toSet())));
This uses Collectors.groupingBy along with Collectors.mapping.

Display only map key from List<Map<String, List<String>>>

I have the following structure:
List<Map<String, List<String>>> filters
Consider the following example:
filters=[{product=[A1, A2, A3]}]
I want to display just the map key and not the values.
Expected output :
product
I tried the following:
String op = filters.get(i).keySet().toString();
This gives me the following output:
[product]
I tried using .stream() as well but that didn't work. I just want to display the keys(one or many) i.e. in this case : product
Any help would be appreciated.
You can use :
filters.stream().flatMap(c -> c.keySet().stream())
.forEach(System.out::println);
Edit:
I want to pass the output received example: (product) to another
function. Would that be done in the .forEach part? I don't want to
print it there
No instead in this case you can collect the result in a List then return it for example :
public List<String> myFunction(List<Map<String, List<String>>> filters){
return filters.stream()
.flatMap(c -> c.keySet().stream())
.collect(Collectors.toList());
}
Then you can consume it like this :
List<String> result = myFunction(filters);
result.forEach(System.out::println);
This should do the trick
List<String> collect = filters.stream()
.flatMap(entry ->
entry.keySet().stream())
.collect(Collectors.toList());
collect will have all the keys of the map.

Java 8 reverse of grouping by

I'm trying to do a simple thing but I'm a bit stuck.
I have the following :
Map<Date, Collection<String>>
So, for one Date I have a list of String (not a unique list).
What I need is to transform this to the following format:
List<Object<Date,String>>
Each element of the first list could be present several times
(Ex of my main list: (date1, string1), (date1, string1), (date2, string2), (date2, string3))
I'm trying to do so with java stream but is not so obvious.
Any idea ?
Thanks.
EDIT: Here is what I have so far
Map<Date, Collection<MyObject>> result = new HashMap<>();
......adding elements to the map...
result.entrySet().stream().
collect(Collectors.toMap(
e -> e.getKey(),
v -> v.getValue().stream().map( p -> aggregateProductService.findProduct(customer.getPublicId(), p, setId)).
filter(Optional::isPresent).
map(Optional::get).
collect(toList())
)
).entrySet().stream().sorted(Map.Entry.comparingByKey()).flatMap(e -> e.getValue().stream().map(s -> Pair.of(e.getKey(), s))).
limit(10).
collect(toList()).stream().
collect(Collectors.groupingBy(Pair::getLeft, Collectors.toList()));
This piece of code does not compile. I have the following error:
Pair::getLeft "non static method cannot be referenced from static context"
If I understood correctly:
map.entrySet()
.stream()
.flatmap(e -> e.getValue().stream().map(s -> Pair.of(e.getKey(), s)))
.collect(Collectors.toList());

Categories