Reputation: 733
I have a data structure in the form of list of maps:
List< Map<String, List<String>> >
I want to collect all the elements of lists (values of maps) in a single Set
using java 8 features.
Example:
Input: [ {"a" : ["b", "c", "d"], "b" : ["a", "b"]}, {"c" : ["a", "f"]} ]
Output: ["a", "b", "c", "d", "f"]
Thanks.
Upvotes: 9
Views: 17560
Reputation: 6706
There are a lot of alternatives to this problem. Here are a few including two of the other answers provided using Eclipse Collections containers.
MutableList<MutableMap<String, MutableList<String>>> list =
Lists.mutable.with(
Maps.mutable.with(
"a", Lists.mutable.with("b", "c", "d"),
"b", Lists.mutable.with("a", "b")),
Maps.mutable.with(
"c", Lists.mutable.with("a", "f")));
ImmutableSet<String> expected = Sets.immutable.with("a", "b", "c", "d", "f");
// Accepted Streams solution
Set<String> stringsStream = list.stream()
.map(Map::values)
.flatMap(Collection::stream)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
Assert.assertEquals(expected, stringsStream);
// Lazy Eclipse Collections API solution
MutableSet<String> stringsLazy = list.asLazy()
.flatCollect(MutableMap::values)
.flatCollect(e -> e)
.toSet();
Assert.assertEquals(expected, stringsLazy);
// Eager Eclipse Collections API solution
MutableSet<String> stringsEager =
list.flatCollect(
map -> map.flatCollect(e -> e),
Sets.mutable.empty());
Assert.assertEquals(expected, stringsEager);
// Fused collect operation
Set<String> stringsCollect = list.stream()
.collect(
HashSet::new,
(set, map) -> map.values().forEach(set::addAll),
Set::addAll);
Assert.assertEquals(expected, stringsCollect);
// Fused injectInto operation
MutableSet<String> stringsInject =
list.injectInto(
Sets.mutable.empty(),
(set, map) -> set.withAll(map.flatCollect(e -> e)));
Assert.assertEquals(expected, stringsInject);
Note: I am a committer for Eclipse Collections.
Upvotes: 4
Reputation: 298599
An alternative to the .flatMap
based solutions, is to fuse these sub-iterations into the final collect
operation:
Set<String> output = input.stream()
.collect(HashSet::new, (set,map) -> map.values().forEach(set::addAll), Set::addAll);
Upvotes: 5
Reputation: 82531
Use flatMap
for this purpose
List< Map<String, List<String>> > maps = ...
Set<String> result = maps.stream()
.flatMap(m -> m.values().stream())
.flatMap(List::stream)
.collect(Collectors.toSet());
Upvotes: 6
Reputation:
You can use a series of Stream.map and Stream.flatMap:
List<Map<String, List<String>>> input = ...;
Set<String> output = input.stream() // -> Stream<Map<String, List<String>>>
.map(Map::values) // -> Stream<List<List<String>>>
.flatMap(Collection::stream) // -> Stream<List<String>>
.flatMap(Collection::stream) // -> Stream<String>
.collect(Collectors.toSet()) // -> Set<String>
;
Upvotes: 19