Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stream groupingBy by one field then merge all others

I have trouble with stream groupingby.

List<FAR> listFar = farList.stream().filter(f -> !f.getStatus().equals(ENUM.STATUS.DELETED))
            .collect(Collectors.toList());
List<HAUL> haulList = listFar.stream().map(f -> f.getHaul()).flatMap(f -> f.stream())
            .collect(Collectors.toList());

It groups by specie, it's all fine, but there are another attributes to HAUL.

Map<Specie, List<HAUL>> collect = haulList.stream().collect(Collectors.groupingBy(HAUL::getSpecie));

Attributes:

  1. haul.getFishCount(); (Integer)
  2. haul.getFishWeight(); (BigDecimal)

Is it possible to group by HAUL::getSpecie (by Specie), but also "merging" together those two extra fields, so I have total?

For example: I have 3 of HAUL elements where fish specie A has 50/30/10 kg in weight.

Can I group it by specie and have total weight?

like image 745
Kefirchiks Avatar asked Dec 14 '22 22:12

Kefirchiks


2 Answers

If I understood correctly:

haulsList 
      .stream()
      .collect(Collectors.groupingBy(HAUL::getSpecie, 
              Collectors.collectingAndThen(Collectors.toList(),
                  list -> {
                     int left = list.stream().mapToInt(HAUL::getFishCount).sum();
                     BigDecimal right = list.stream().map(HAUL::getFishWeight).reduce(BigDecimal.ZERO, (x, y) -> x.add(y));
                    return new AbstractMap.SimpleEntry<>(left, right);
                  })));

There is a form to do:

.stream()
.collect(Collectors.groupingBy(HAUL::getSpecie, 
              Collectors.summingInt(HAUL::getFishCount)));

or

 .stream()
 .collect(Collectors.groupingBy(HAUL::getSpecie,
             Collectors.mapping(HAUL::getFishWeight, Collectors.reducing((x, y) -> x.add(y)))));

But you can't really make these to act at the same time.

like image 176
Eugene Avatar answered Dec 27 '22 15:12

Eugene


You might use mapping and reduce for example:

class Foo {   int count;   double weight;   String spice; } 
List<Foo> fooList = Arrays.asList(
    new Foo(1,new BigDecimal(10), "a"),
    new Foo(2,new BigDecimal(38), "a"),
    new Foo(5,new BigDecimal(2), "b"),
    new Foo(4,new BigDecimal(8), "b"));

Map<String,Optional<BigDecimal>> spieceWithTotalWeight = fooList.stream().
        collect(
                groupingBy(
                        Foo::getSpice,
                        mapping(
                                Foo::getWeight, 
                                Collectors.reducing(BigDecimal::add)
                        )
                )
        );
System.out.println(spieceWithTotalWeight); // {a=Optional[48], b=Optional[10]}

I hope this helps.

like image 30
Anton Balaniuc Avatar answered Dec 27 '22 14:12

Anton Balaniuc