Reputation: 1576
I have a list (GlobalBooks) which looks similar to the below
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
GlobalBooks globalBooks = new GlobalBooks();
List<Book> bookList = new ArrayList<Book>();
Book book = new Book();
List<BookContent> bookContents = new ArrayList<BookContent>();
book.setBookName("A");
BookContent content = new BookContent();
content.setDescription("December 2016");
content.setComponentID(20l);
bookContents.add(content);
content = new BookContent();
content.setDescription("January 2016");
content.setComponentID(30l);
bookContents.add(content);
content = new BookContent();
content.setDescription("Febuary 2016");
content.setComponentID(40l);
bookContents.add(content);
book.setContents(bookContents);
bookList.add(book);
book = new Book();
bookContents = new ArrayList<BookContent>();
book.setBookName("B");
content = new BookContent();
content.setDescription("December 2016");
content.setComponentID(20l);
bookContents.add(content);
content = new BookContent();
content.setDescription("January 2016");
content.setComponentID(30l);
bookContents.add(content);
content = new BookContent();
content.setDescription("Febuary 2016");
content.setComponentID(40l);
bookContents.add(content);
book.setContents(bookContents);
bookList.add(book);
globalBooks.setBooks(bookList);
System.out.println(globalBooks);
}
}
I am looking for java 8 functions which could stream the globalBooks response and collect a map of List of Books based on the description field in the BooksContent.
Map<String,List<Books>>
i.e. all the books with the same description should be grouped together by description ?
I can do this via regular java but the code becomes too messy and untidy.
Upvotes: 2
Views: 6092
Reputation: 3615
This will do the job:
Map<String, List<String>> result = bookList.stream().flatMap(b -> b.getContents().stream())
.collect(Collectors.toMap(str -> str, str -> bookList.stream()
.filter(b -> b.getContents().contains(str))
.collect(Collectors.toList()), (a, b) -> {a.addAll(b);return a;}));
If you want to execute this in parallel, simple replace the collector with Collectors.toConcurrentMap
.
Upvotes: 1
Reputation: 4959
Since each book has multiple contents with different descriptions, you would need each book to appear in multiple map entries. So I would use a flatMap
to create a stream element for each BookContent, specifically a Description+Book pair. Then collect into a map as usual with Collectors.groupingBy
:
Map<String,List<Book>> result = globalBooks.getBooks().stream().flatMap(
(Book b) -> b.getContents().stream().map(
(BookContent bc) -> new Pair<>(bc.getDescription(), b)
)
).collect(Collectors.groupingBy(
Pair::getKey,
Collectors.mapping(Pair::getValue, Collectors.toList())
));
Note:
The Pair class I have used above is from javafx.util.Pair
. However you could just as easily use AbstractMap.SimpleEntry
, your own Pair class, or any collection data type capable of holding two Objects.
Upvotes: 6
Reputation: 335
Another solution could be
Map<String,List<Book>> result = new HashMap<String, List<Book>>();
globalBooks.getBooks().stream().forEach(book->{
book.getContents().stream().forEach(bookContent->{
result.computeIfAbsent(bookContent.getDescription(),(list)->new ArrayList<Book>()).add(book);
});
});
Upvotes: 1