I have class Data which have 4 fields and have list of values then i want replace each row groupby year and sum value for same year using java 8 and stream. please see below code and output.
class Data {
int id;
int year;
String name;
double value;
}
List<Data> list = new ArrayList<>();
list.add(new Data(101, 2018, "AAA", 20));
list.add(new Data(102, 2019, "BBB", 30));
list.add(new Data(103, 2020, "CCC", 10));
list.add(new Data(104, 2019, "DDD", 50));
list.add(new Data(105, 2020, "EEE", 40));
list.add(new Data(106, 2020, "FFF", 60));
First code try:
list.stream().collect(Collectors.groupingBy(Data::getYear, Collectors
.summingDouble(Data::getValue)));
Second code try:
list.stream().collect(Collectors.toMap(value -> value.getYear(), Function.identity(),
(a, b) -> new Data(a.getId(), a.getYear(), a.getName(), a.getValue() b.getValue()))).values()
.forEach(value -> System.out.println(value));
Output:
id year name value
101 2018 "AAA" 20
102 2019 "BBB" 80
103 2020 "CCC" 110
Expected Output:
id year name value
101 2018 "AAA" 20
102 2019 "BBB" 80
103 2020 "CCC" 110
104 2019 "DDD" 80
105 2020 "EEE" 110
106 2020 "FFF" 110
CodePudding user response:
Map created by the second stream in your code preserves only a single entry per year.
Remaping function (a, b) -> new Data(a.getId(), a.getYear(), a.getName(), a.getValue() b.getValue())) takes care about the duplicates so that value of each data object will be the sum of all values.
In order to retain all the Data and change the value of every object to be a total value for the given year, you have to take several steps:
- create a map:
year-->total value per year(which is already done correcl); - create a map:
year-->list of Dataobjects; - apply the
total value per yearto each of theDataobjects.
public static void main(String[] args) {
List<Data> dataList = List.of(
new Data(101, 2018, "AAA", 20),
new Data(102, 2019, "BBB", 30),
new Data(103, 2020, "CCC", 10),
new Data(104, 2019, "DDD", 50),
new Data(105, 2020, "EEE", 40),
new Data(106, 2020, "FFF", 60)
);
Map<Integer, Double> yearToTotalVal = getTotalValueMap(dataList);
Map<Integer, List<Data>> yearToData = getYearToDataListMap(dataList);
appllyTotalValue(yearToTotalVal, yearToData);
for (Map.Entry<Integer, List<Data>> entry: yearToData.entrySet()) {
System.out.println(entry);
}
}
private static Map<Integer, Double> getTotalValueMap(List<Data> dataList) {
return dataList.stream()
.collect(Collectors.groupingBy(Data::getYear,
Collectors.summingDouble(Data::getValue)));
}
private static Map<Integer, List<Data>> getYearToDataListMap(List<Data> dataList) {
return dataList.stream()
.collect(Collectors.groupingBy(Data::getYear));
}
private static void appllyTotalValue(Map<Integer, Double> yearToTotalVal,
Map<Integer, List<Data>> yearToData) {
for (Integer year: yearToTotalVal.keySet()) {
yearToData.get(year)
.replaceAll(data -> new Data(data.getId(),
data.getYear(),
data.getName(),
yearToTotalVal.get(year)));
}
}
output - data for each year:
2018=[Data{id=101, year=2018, name='AAA', value=20.0}]
2019=[Data{id=102, year=2019, name='BBB', value=80.0}, Data{id=104, year=2019, name='DDD', value=80.0}]
2020=[Data{id=103, year=2020, name='CCC', value=110.0}, Data{id=105, year=2020, name='EEE', value=110.0}, Data{id=106, year=2020, name='FFF', value=110.0}]
Side note:
- I also advise you to make the
Data class immutable
public class Data {
private final int id;
private final int year;
private final String name;
private final double value;
// Constractor, geters, etc
}
