How to flatten map values using java streams - java

I am new to Java streams and have a problem at hand. I have a map like this:
Map<String, List<String>> specialProductsMap
And i want to flatten the map values to a set which contains all the String values in lists in the specialProductsMap. How can i do this using Java Streams?

You may use the flatMap operator to get this thing done. Here's how it looks.
Set<String> valueSet = specialProductsMap.values().stream()
.flatMap(List::stream)
.collect(Collectors.toSet());

First Obtain the list of values from map then use stream api like this
Set<String> setOfString = specialProductsMap.values().stream().flatMap(list->list.stream())
.collect(Collectors.toSet());
Or Like this Using Method reference
Set<String> setOfString = specialProductsMap.values().stream().flatMap(List::stream)
.collect(Collectors.toSet());

You have to stream your values :
Stream<List<String>> myStream = specialProductsMap.values().stream();
Then flatten it :
Stream<String> myData = myStream.flatMap(List::stream);
Then collect in a set :
Set<String> = myData.collect(Collectors.toSet());

Related

From Map<String, Set<Integer>> collect values from a Set<String> of keys

I am trying to use map to map a set of keys into a Map of String to Set of Integer. Ideally I want to get all the value sets and collect them into a single set.
Lets say I have:
Map<String, List<Integer>> keyValueMap = new HashMap<>();
Set<String> keys = new HashSet<>();
Set<String> result = new HashSet<>();
I have tried:
result.addAll(keys.stream().map(key -> keyValueMap.get(key)).collect(Collectors.toSet());
This nets me an error saying addAll() is not applicable for the type Set>. I have tried replacing map() with flatMap() but I can't seem to get the syntax right if that is the solution.
What is the correct syntax to make this work?
Thanks!
It looks like the type of result should be Set<Integer> instead of Set<String>.
With your snippet, you're attempting to invoke Set#addAll on a Set<Integer>, but the argument being passed is a Set<List<Integer>>, which doesn't compile.
To ameliorate your issue, one solution is to use flatMap instead of map:
result.addAll(keys.stream()
.flatMap(key -> keyValueMap.get(key).stream())
.collect(Collectors.toSet()));
A logically equivalent snippet is:
result.addAll(keys.stream()
.map(keyValueMap::get)
.flatMap(List::stream)
.collect(Collectors.toSet()));
Another solution would be to utilize Map#values:
result.addAll(keyValueMap.values().stream()
.flatMap(List::stream)
.collect(Collectors.toSet()));

How to get list of Pojos from collection of map

Collection<Map<String, MyObj>>
I need to collect list of MyObj from above structure.
For instance I also had Collection<MyObj> - in this for collecting list of MyObj I did below
List<MyObj> result = new ArrayList<>(MyObj);
works fine.
How do I achieve similar result from Collection<Map<String, MyObj>>?
So you want to flatten the maps to a single list of values? You can use streams to do this pretty easily:
List<MyObj> list = collection.stream()
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.toList());
You can do:
List<MyObj> result = collections.stream()
.flatMap(m->m.values().stream())
.collect(toList());
Try this:
Takes the map values and streams them and collects them into a list.
List<MyObj> obList = origList.stream()
.flatMap(m->m.values().stream())
.collect(Collectors.toList());

Grouping By without using a POJO in java 8

I have a use case where I need to read a file and get the grouping of a sequence and a list of values associated with the sequence. The format of these records in the file are like sequence - val , example
10-A
10-B
11-C
11-A
I want the output to be a map (Map<String,List<String>>) with the sequence as the key and list of values associated with it as value, like below
10,[A,B]
11,[C,A]
Is there a way I can do this without creating a POJO for these records? I have been trying to explore the usage of Collectors.groupingBy and most of the examples I see are based on creating a POJO.
I have been trying to write something like this
Map<String, List<String>> seqCpcGroupMap = pendingCpcList.stream().map(rec ->{
String[] cpcRec = rec.split("-");
return new Tuple2<>(cpcRec[0],cpcRec[1])
}).collect(Collectors.groupingBy(x->x.))
or
Map<String, List<String>> seqCpcGroupMap = pendingCpcList.stream().map(rec ->{
String[] cpcRec = rec.split("-");
return Arrays.asList(cpcRec[0],cpcRec[1]);
}).collect(Collectors.groupingBy(x->(ArrayList<String>)x[0]));
I am unable to provide any key on which the groupingBy can happen for the groupingBy function, is there a way to do this or do I have to create a POJO to use groupingBy?
You may do it like so,
Map<String, List<String>> result = source.stream()
.map(s -> s.split("-"))
.collect(Collectors.groupingBy(a -> a[0],
Collectors.mapping(a -> a[1], Collectors.toList())));
Alternatively, you can use Map.computeIfAbsent directly as :
List<String> pendingCpcList = List.of("10-A","10-B","11-C","11-A");
Map<String, List<String>> seqCpcGroupMap = new HashMap<>();
pendingCpcList.stream().map(rec -> rec.split("-"))
.forEach(a -> seqCpcGroupMap.computeIfAbsent(a[0], k -> new ArrayList<>()).add(a[1]));

How to use Java Streams to fetch Strings from an ArrayList of HashMaps

I have a Data structure - ArrayList> and this is what I need to do -
ArrayList<HashMap<String, String>> optMapList;
//populated optMapList with some code. Not to worry abt this
List<String> values = new ArrayList<String>();
for(HashMap<String,String> entry: optMapList){
values.add(entry.get("optValue"));
}
How do we use Java Streams to achieve the same objective?
optMapList.stream()
.filter(Objects:nonNull) // potentially filter null maps
.map(m -> m.get("optValue"))
.filter(Objects::nonNull) // potentially filter null values form the map
// .collect(Collectors.toCollection(ArrayList::new))
.collect(Collectors.toList())

Map to List after filtering on Map's key using Java8 stream

I have a Map<String, List<String>>. I want to transform this map to a List after filtering on the map's key.
Example:
Map<String, List<String>> words = new HashMap<>();
List<String> aList = new ArrayList<>();
aList.add("Apple");
aList.add("Abacus");
List<String> bList = new ArrayList<>();
bList.add("Bus");
bList.add("Blue");
words.put("A", aList);
words.put("B", bList);
Given a key, say, "B"
Expected Output: ["Bus", "Blue"]
This is what I am trying:
List<String> wordsForGivenAlphabet = words.entrySet().stream()
.filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
.map(x->x.getValue())
.collect(Collectors.toList());
I am getting an error. Can someone provide me with a way to do it in Java8?
Your sniplet wil produce a List<List<String>> not List<String>.
You are missing flatMap , that will convert stream of lists into a single stream, so basically flattens your stream:
List<String> wordsForGivenAlphabet = words.entrySet().stream()
.filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
.map(Map.Entry::getValue)
.flatMap(List::stream)
.collect(Collectors.toList());
You can also add distinct(), if you don't want values to repeat.
Federico is right in his comment, if all you want is to get the values of a certain key (inside a List) why don't you simply do a get (assuming all your keys are uppercase letters already) ?
List<String> values = words.get(inputAlphabet.toUpperCase());
If on the other hand this is just to understand how stream operations work, there is one more way to do it (via java-9 Collectors.flatMapping)
List<String> words2 = words.entrySet().stream()
.collect(Collectors.filtering(x -> x.getKey().equalsIgnoreCase(inputAlphabet),
Collectors.flatMapping(x -> x.getValue().stream(),
Collectors.toList())));
As was previously told after collect you will get List<List<String>> with only one or zero value in it. You can use findFirst instead of collect it will return you Optional<List<String>>.

Categories