Java 8 streams merge inner stream result to stream above - java

Maybe it's perversion but I want to merge results of inner stream to stream on the level above.
For example we have some complex map with data:
Map<String, List<Map<String, Object>>> dataMap
and I need to collect all Objects to List. For now I'm doing like this:
Set<Object> segmentIds = new HashSet<>();
dataMap.values().forEach(maps -> maps.forEach(map -> segmentIds.add(map.get("object"))));
But it's not prettily way. But I can't understand how to transfer data from inner cycle to outer to collect them in the end.
Is it possible to do it without any outer objects?

What about it:
Set<Object> collect = dataMap.values()
.stream()
.flatMap(Collection::stream)
.map(map -> map.get("object"))
.collect(Collectors.toSet());

You have to use flatMapof the Stream-API.
List<Object> allObjects = dataMap.values().stream()
.flatMap(l -> l.stream())
.flatMap(m -> m.values().stream())
.collect(Collectors.toList())
Code is not tested

Related

Convert stream of List<Object> to stream of all Objects in the List

The following lambda expressions take the values of a HashMap, gets a List of Objects in its ArrayList, adds all such Objects to another ArrayList, and then prints each attribute if a condition is met.
This works, but I am a bit frustrated I couldn't figure out how to do this in one step, as in, not using two lambda expressions.
Map<Integer, Person> people = new HashMap<Integer, Person>();
...
List<Object> objects = new ArrayList<Object>();
people.values().stream()
.map(p->p.getObjects())
.forEach(p->objects.addAll(p)); //note: can be multiple
objects.stream()
.filter(p->p.getClass().toString().contains("Keyword"))
.forEach(p->System.out.println(p.display()));
So is there a way I can go from line 2 to line 5 directly, which would in effect convert a stream of List of Objects to a stream of all of the Objects themselves?
You could merge your operations to a single stream pipeline as
List<Pet> cats = people.values().stream()
.flatMap(p -> p.getPets().stream())
.filter(p -> p.getClass().toString().contains("Cat")) // or Cat.class::isInstance
.collect(Collectors.toList());
and then perform operations on them as in your code such as
cats.forEach(cat -> System.out.println(cat.getName()));
An overall transformation of your code would look like:
Map<Integer, Person> people = ...;
people.values().stream()
.flatMap(p -> p.getPets().stream())
.filter(p -> p.getClass().toString().contains("Cat"))
.forEach(cat -> System.out.println(cat.getName()));

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.

Java 8 stream: parse list of maps

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());

java streams - how to flat all values from map of collections using condition on the key

I have a Map. Let's say
Map<Long, List<MyObj>>
I want to create a long array where of all MyObj where the key (long) is found in another set()
anotherSet.contains(long)
using java stream.
I tried
map.entrySet()
.stream()
.filter(e->anotherSet(e.getKey()))
.flatMap(e.getValue)
.collect(Collectors.toList);
But it doesnt even compile
You had a few syntax errors.
This should produce your desired List :
List<MyObj> filteredList =
map.entrySet()
.stream()
.filter(e->anotherSet.contains(e.getKey())) // you forgot contains
.flatMap(e-> e.getValue().stream()) // flatMap requires a Function that
// produces a Stream
.collect(Collectors.toList()); // you forgot ()
If you want to produce an array instead of a List, use :
MyObj[] filteredArray =
map.entrySet()
.stream()
.filter(e->anotherSet.contains(e.getKey()))
.flatMap(e-> e.getValue().stream())
.toArray(MyObj[]::new);

How to convert Map to List in Java 8

How to convert a Map<String, Double> to List<Pair<String, Double>> in Java 8?
I wrote this implementation, but it is not efficient
Map<String, Double> implicitDataSum = new ConcurrentHashMap<>();
//....
List<Pair<String, Double>> mostRelevantTitles = new ArrayList<>();
implicitDataSum.entrySet()
.stream()
.sorted(Comparator.comparing(e -> -e.getValue()))
.forEachOrdered(e -> mostRelevantTitles.add(new Pair<>(e.getKey(), e.getValue())));
return mostRelevantTitles;
I know that it should works using .collect(Collectors.someMethod()). But I don't understand how to do that.
Well, you want to collect Pair elements into a List. That means that you need to map your Stream<Map.Entry<String, Double>> into a Stream<Pair<String, Double>>.
This is done with the map operation:
Returns a stream consisting of the results of applying the given function to the elements of this stream.
In this case, the function will be a function converting a Map.Entry<String, Double> into a Pair<String, Double>.
Finally, you want to collect that into a List, so we can use the built-in toList() collector.
List<Pair<String, Double>> mostRelevantTitles =
implicitDataSum.entrySet()
.stream()
.sorted(Comparator.comparing(e -> -e.getValue()))
.map(e -> new Pair<>(e.getKey(), e.getValue()))
.collect(Collectors.toList());
Note that you could replace the comparator Comparator.comparing(e -> -e.getValue()) by Map.Entry.comparingByValue(Comparator.reverseOrder()).
Note that if you want efficient implementation, you should consider this:
List<Pair<String, Double>> mostRelevantTitles =
implicitDataSum.entrySet()
.stream()
.map(e -> new Pair<>(e.getKey(), e.getValue()))
.collect(Collectors.toList());
mostRelevantTitles.sort(Comparators.comparing(Pair::getSecond, Comparator.reverseOrder()));
I assume that your Pair class have getSecond getter.
Using the sorted() stream pipeline step you create intermediate buffer, store everything to that buffer, convert it into array, sort that array, then store the result into the ArrayList. My approach, though less functional, stores data directly into the target ArrayList, then sorts it in-place without any additional copying. So my solution would take less time and intermediate memory.
public List<TeamResult> process(final Map<String, Team> aggregatedMap) {
return aggregatedMap.entrySet()
.stream()
.map(e -> new TeamResult(e.getKey(),e.getValue()))
.collect(Collectors.toList());
}
Sort the Map based on values in reverse order and collect the keys in list and also limit only first 2 results in the list
List<String> list = map.keySet().stream()
.sorted((k1, k2)->map.get(k2)- map.get(k1))
.limit(2)
.collect(Collectors.toList())

Categories