Java 8 reverse of grouping by - java

I'm trying to do a simple thing but I'm a bit stuck.
I have the following :
Map<Date, Collection<String>>
So, for one Date I have a list of String (not a unique list).
What I need is to transform this to the following format:
List<Object<Date,String>>
Each element of the first list could be present several times
(Ex of my main list: (date1, string1), (date1, string1), (date2, string2), (date2, string3))
I'm trying to do so with java stream but is not so obvious.
Any idea ?
Thanks.
EDIT: Here is what I have so far
Map<Date, Collection<MyObject>> result = new HashMap<>();
......adding elements to the map...
result.entrySet().stream().
collect(Collectors.toMap(
e -> e.getKey(),
v -> v.getValue().stream().map( p -> aggregateProductService.findProduct(customer.getPublicId(), p, setId)).
filter(Optional::isPresent).
map(Optional::get).
collect(toList())
)
).entrySet().stream().sorted(Map.Entry.comparingByKey()).flatMap(e -> e.getValue().stream().map(s -> Pair.of(e.getKey(), s))).
limit(10).
collect(toList()).stream().
collect(Collectors.groupingBy(Pair::getLeft, Collectors.toList()));
This piece of code does not compile. I have the following error:
Pair::getLeft "non static method cannot be referenced from static context"

If I understood correctly:
map.entrySet()
.stream()
.flatmap(e -> e.getValue().stream().map(s -> Pair.of(e.getKey(), s)))
.collect(Collectors.toList());

Related

How to collect data to List<Object> from Map<Object,Integer> using java Stream API?

I have map Map<Nominal, Integer> with objects and their counts:
a -> 3
b -> 1
c -> 2
And I need to get such a List<Nominal> from it:
a
a
a
b
c
c
How can I do this using the Stream API?
We can use Collections::nCopies to achieve the desired result:
private static <T> List<T> transform(Map<? extends T, Integer> map) {
return map.entrySet().stream()
.map(entry -> Collections.nCopies(entry.getValue(), entry.getKey()))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
Ideone demo
Remark
In the demo, I changed the key-type of the Map from Nominal to Object since the definition of Nominal was not provided. Changing the key-type, however, does not influence the solution.
Stream the entries and use flatMap to generate multiple copies of each key based on the value.
List<Nominal> expanded = map.entrySet().stream()
.flatMap(e -> generate(e::getKey).limit(e.getValue()))
.collect(toList());

how to convert a hashmap of list into a single list in Java 8?

I am completely new to java 8 and i am a bit unclear on how to proceed.
i have a Map <String, List<value>> in Java 7, i would just use for loop on the keys and collect the List into a single list.
however, i want to be able to do this in 8.
what i have is:
List<Value> newList = resultMap.entrySet().stream()
.flatMap( e -> e.getValue().stream())
.map( // get the value in the list)
.collect(Collectors.toList())
However, in this case, i would not be able to know the key from the hashmap which the value belongs to.
How can i get the value of the key for the hashmap while doing the above?
You can do something like this:
Map<Key, List<Value>> map = ...;
List<Map.Entry<Key, Value>> list =
map.entrySet()
.stream()
.flatMap(e -> {
return e.getValue().stream()
.map(v -> new AbstractMap.SimpleEntry<>(e.getKey(), v));
})
.collect(Collectors.toList());
This creates an entry for each value in each sublist, where the key is the corresponding key for the list that value came from.

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

Java8 streams : Transpose map with values as list

I have map with key as String and value as List. List can have 10 unique values. I need to convert this map with key as Integer and value as List. Example as below :
Input :
"Key-1" : 1,2,3,4
"Key-2" : 2,3,4,5
"Key-3" : 3,4,5,1
Expected output :
1 : "Key-1","Key-3"
2 : "Key-1","Key-2"
3 : "Key-1", "Key-2", "Key-3"
4 : "Key-1", "Key-2", "Key-3"
5 : "Key-2", "Key-3"
I am aware that using for loops i can achieve this but i needed to know can this be done via streams/lamda in java8.
-Thanks.
An idea could be to generate all value-key pairs from the original map and then group the keys by these values:
import java.util.AbstractMap.SimpleEntry;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
...
Map<Integer, List<String>> transposeMap =
map.entrySet()
.stream()
.flatMap(e -> e.getValue().stream().map(i -> new SimpleEntry<>(i, e.getKey())))
.collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())));
Alexis’ answer contains the general solution for this kind of task, using flatMap and a temporary holder for the combination of key and flattened value. The only alternative avoiding the creation of the temporary holder objects, is to re-implement the logic of the groupingBy collector and inserting the loop over the value list logic into the accumulator function:
Map<Integer, List<String>> mapT = map.entrySet().stream().collect(
HashMap::new,
(m,e) -> e.getValue().forEach(
i -> m.computeIfAbsent(i,x -> new ArrayList<>()).add(e.getKey())),
(m1,m2) -> m2.forEach((k,v) -> m1.merge(k, v, (l1,l2)->{l1.addAll(l2); return l1;})));
It's a bit scary (I generally try to break it down to make it more readable) but you could do it this way:
Map<Integer, List<String>> transposeMap = new HashMap<>();
map.forEach((key, list) -> list.stream().forEach(
elm -> transposeMap.put(elm,
transposeMap.get(elm) == null ? Arrays.asList(key) : (Stream.concat(transposeMap.get(elm).stream(),
Arrays.asList(key).stream()).collect(Collectors.toList())))));
Assuming Map<String, List<Integer>> map is your original Map that you want to transpose. transposeMap will have transposed map that you need.
You can Achieve in this way
Let suppose I have Person class with Gender and Age . I want to get it in this form
Map<SEX,List<Person>>
I would write simply
Map<SEX,List<Person>> map = personList.stream()
.collect(Collectors.groupingBy(Person::getGender));
it will get me something like below (one key against multiple values )
key:MALE
age31sexMALE
age28sexMALE
key:FEMALE
age40sexFEMALE
age44sexFEMALE
with teeing You can work on keys and values in 2 streams separately
since Java 12
Map<Integer, List<String>> to = from.entrySet().stream()
.collect(teeing(flatMapping(e -> e.getValue().stream(), toList()),
flatMapping(e -> (Stream<String>)e.getValue().stream().map(i -> e.getKey()), toList()),
(k, v) -> {
return IntStream.range(0, k.size()).boxed().collect(
groupingBy(i -> k.get(i), mapping(i -> v.get(i), toList())));
}));

Joining a List<String> inside a map

I'm trying to convert a Map<String, List<String>> to a Map<String, String>, where the value for each key is the joint string built by joining all the values in the List in the previous map, e.g.:
A -> ["foo", "bar", "baz"]
B -> ["one", "two", "three"]
should be converted to
A -> "foo|bar|baz"
B -> "one|two|three"
What's the idiomatic way to do this using the Java 8 Streams API?
Simply use String.join, no need to create the nested stream:
Map<String, String> result = map.entrySet()
.stream()
.collect(toMap(
e -> e.getKey(),
e -> String.join("|", e.getValue())));
You can use Collectors.joining(delimiter) for this task.
Map<String, String> result = map.entrySet()
.stream()
.collect(toMap(
Map.Entry::getKey,
e -> e.getValue().stream().collect(joining("|")))
);
In this code, each entry in the map is collected to a new map where:
the key stays the same
the value, which is a list, is collected to a String by joining all the elements together
Google Guava has a nice helper method for this:
com.google.common.collect.Maps.transformValues(map, x -> x.stream().collect(joining("|")));
using pure java, this would work:
map.entrySet().stream().collect(toMap(Entry::getKey, e -> e.getValue().stream().collect(joining("|"))));

Categories