Stream groupingBy: reducing to first element of list
Solution 1
Actually, you need to use Collectors.toMap
here instead of Collectors.groupingBy
:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.toMap(Valuta::getCodice, Function.identity()));
groupingBy
is used to group elements of a Stream based on a grouping function. 2 Stream elements that will have the same result with the grouping function will be collected into a List
by default.
toMap
will collect the elements into a Map
where the key is the result of applying a given key mapper and the value is the result of applying a value mapper. Note that toMap
, by default, will throw an exception if a duplicate is encountered.
Solution 2
It's a bit late in the game, but try this:
Map<String, Valuta> map =
getValute().stream()
.collect(Collectors.groupingBy(Valuta::getCodice,
Collectors.collectingAndThen(
Collectors.toList(),
values -> values.get(0))));
Solution 3
You could use Collectors.toMap(keyMappingFunction, valueMappingFunction)
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v));
You can replace v->v
with Function.identity()
if you find it more readable. Note that toMap
, by default, will throw an exception if a duplicate is encountered.
Solution 4
The toMap
version which opts to choose the 1st value on collisions instead of throwing an exception is:
Collectors.toMap(keyMapper, valueMapper, mergeFunction)
ex:
Map<String, Valuta> map = list
.stream()
.collect(Collectors.toMap(Valuta::getCodice, v -> v, (v1, v2) -> v1));
Solution 5
Not completely related to this question but quiet similar case .If you want to group list by multiple params and after that need return object of different type as value then you can try this solution . Hope helps to someone!
public Map<Integer, Map<String, ObjectTypeB>> setObjectTypeB(List<ObjectTypeA> typeAList) {
Map<Integer, Map<String, ObjectTypeB>> map = typeAList.stream()
.collect(groupingBy(ObjectTypeA::getId,
groupingBy(ObjectTypeA::getDate,
collectingAndThen(mapping(this::getObjectTypeB,toList()),values -> values.get(0)))));
return map;
}
public ObjectTypeB getObjectTypeB(ObjectTypeA typeA) {
ObjectTypeB typeB = new ObjectTypeB();
typeB.setUnits(typeA.getUnits());
return typeB;
}
Comments
-
Fabio B. almost 2 years
I have a
List<Valuta>
which can be represented (simplified) JSON-style:[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD, description=Dollars, ratio=1.1 } ]
I want to transform that in a
Map<String, Valuta>
like this:{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, description=Dollars, ratio=1.1 }}
I wrote this one-liner:
getValute().stream().collect(Collectors.groupingBy(Valuta::getCodice));
but this returns a
Map<String, List<Valuta>>
instead of what I need.I suppose
mapping()
function would work for me, but don't know how. -
user909481 almost 7 yearsHow to do the same with
groupingBy
? I cannot usetoMap
because I must expect duplicate keys from the key mapper. I seegroupingBy
has signature for downstream collector as 2nd argument but couldn't make it work yet. -
BaiJiFeiLong about 6 yearsThis is what i am finding for
-
fricke over 5 yearsthis is what's needed in case you actually do have duplicates, but don't want to use it
-
user1735921 over 4 yearsLets say I want Map<String, String> instead of Map<String, Valuta>, I need Valuta.getDescription() , basically, is that possible?
-
jocull over 4 yearsBe aware that
Collectors.toMap
will throw an exception if you somehow have duplicate keys! They are not completely equivalent. -
jocull over 4 yearsYou might be able to use the
mergeFunction
of.toMap
to simply drop any duplicates and avoid extra object allocations: stackoverflow.com/a/32313069/97964 -
jocull over 4 yearsDiscovered this - you might be able to use the
mergeFunction
of.toMap
to simply drop or replace any duplicates and avoid extra object allocations: stackoverflow.com/a/32313069/97964 -
toongeorges about 4 yearsWhy create a list in the stream? Here your solution adapted without creating the list:
getValute().stream().collect(groupingBy(Valuta::getCodice, reducing(null, identity(), (first, last) -> last)))
-
rogerdpack about 3 yearsThese appear to be three equivalent ways to perform the default
groupingBy
behavior. Which is interesting but I don't think what the OP wanted (which was a single k -> v). :) -
andresp over 2 yearsDoes the merge function preserve ordering? i.e. is it guaranteed that v1 always appears before v2 in list?
-
3ygun over 2 years@andresp If you're working with
stream()
called on anArrayList
(which uses it's ordered iterator) than yes. Otherwise, it's dependent on your collection see something like stackoverflow.com/a/40583642/6480404