Reputation: 399
Trying to get a map of lists using java 8. I have array of predifined titles:
String[] myStringArray = {"TITLE1", "TITLE2", "TITLE3"};
And a list of some Pages objects (List<String>
).
I need to create a map of lists of objects with keys from the predefined array if the value from this array was found on the page. The result should be like:
{TITLE1=[page1, page2, page3], TITLE2=[page3, page4, page5]}
(let's assume that TITLE3 was not found in Page list)
My code:
Arrays.stream(myStringArray)
.map(title -> page
.stream()
.filter(p -> isPageContainsTitle(p, title))
.collect(Collectors.toList()))
.filter(Objects::nonNull)
.collect(Collectors.toMap(e->e, Function.identity()));
As a result i get Map of lists with list in key instead of value from myStringArray:
{[page1, page2, page3]=[page1, page2, page3], [page3, page4, page5]=[page3, page4, page5]}
Upvotes: 2
Views: 8896
Reputation: 39978
Just make sure array has unique titles and then use Collectors.toMap
Map<String, List<String>> result = Arrays.stream(myStringArray)
.map(title -> new AbstractMap.SimpleEntry<>(title,
pages.stream().filter(page -> isPageContainsTitle(page,title)).collect(Collectors.toList())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
And as @Naman suggested you can avoid the intermediate map
operation
Map<String, List<String>> result = Arrays.stream(myStringArray)
.collect(Collectors.toMap(Function.identity()
,title -> pages.stream()
.filter(page -> isPageContainsTitle(page, title))
.collect(Collectors.toList())));
Upvotes: 3
Reputation: 363
Below code should work for you:
package com.shree.test;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
String[] myStringArray = {"TITLE1", "TITLE2", "TITLE3"};
List<String> pages = new ArrayList<>();
pages.add("TEXT,TITLE2,TEXT,ONE,TWO");
pages.add("TEXT,TITLE1,TEXT,ONE,TWO");
pages.add("TEXT,TITLE2,TEXT,ONE,TWO");
pages.add("TEXT,TITLE2,TEXT,TITLE3,TWO");
pages.add("TEXT,TITLE3,TEXT,ONE,TWO");
Map<Object, Object> titleMap = getTitleMap(myStringArray, pages);
titleMap.entrySet().forEach(System.out::println);
}
private static Map<Object, Object> getTitleMap(String[] myStringArray, List<String> pages) {
Map<Object, Object> titleMap = Arrays.asList(myStringArray).stream()
.map(
(title)->{
return new AbstractMap.SimpleEntry<>(title, pages.stream()
.filter((page)->page.contains(title))
.collect(Collectors.toList()));
}
)
.collect(Collectors.toMap(entry->entry.getKey(), entry->entry.getValue()));
return titleMap;
}
}
Upvotes: 2
Reputation: 40024
The only way I can see to do this with the separate objects it to map them to a two element array of types String
and List<Page>
to an array and then use those entries to build the map.
The Object[]{}
array will contain the title and the list. This was the only way to preserve the two values across nested streams.
I later cast them to create a Map<String,List<Page>>
This requires no modification of your existing data structures.
Map<String, List<Page>> map = Arrays.stream(myStringArray)
.map((String title) -> new Object[] {title,
page.stream().filter(
(Page p) -> isPageContainsTitle(p, title)).collect(
Collectors.toList())})
.filter(arr->arr[1] != null)
.collect(Collectors.toMap(arr -> (String)arr[0], arr -> (List<Page>)arr[1]));
There is a type safety warning in the final List cast but it should be okay.
Upvotes: 0
Reputation: 35141
So
e->e
and
Function.identity()
are and should be equivalent.
What you really want is a flatMap:
Arrays.stream(myStringArray)
.flatMap(title -> pages
.stream()
.filter(p -> isPageContainsTitle(p, title))
.map( p -> new Tuple(t, p))
)
.collect(Collectors.groupingBy( t -> t._1, t -> t._2));
We want flatMap because we may get back 0, 1 or many Tuples of (title, page)
This assumes you have a Tuple class, I'm modelling one based on Scala's Tuple2.
Note, I have not tested this code.
Upvotes: 0