I have the data structre called MyPojo
which has fields called time
, name
and timetaken
(all are in Strings). I'm trying to do some grouping as follows:
List<MyPojo> myPojos = Arrays.asList(
new MyPojo("2017", "ABC", "30"),
new MyPojo("2017", "ABC", "20"),
new MyPojo("2016", "ABC", "25"),
new MyPojo("2017", "XYZ", "40")
);
Map<String, Map<String, Double>> resultMap = myPojos.stream()
.collect(Collectors.groupingBy(MyPojo::getName,
Collectors.groupingBy(MyPojo::getTime,
Collectors.averagingDouble(MyPojo::getTimeTakenAsDouble))));
Please note that I've a method called getTimeTakenAsDouble
to convert thetimetaken
string to double value.
This results as follows:
{ABC={2017=25.0, 2016=25.0}, XYZ={2017=40.0}}
However, my frontend developer wanted the data either in the following format:
{ABC={2017=25.0, 2016=25.0}, XYZ={2017=40.0, 2016=0.0}}
or
[
{
"time": "2017",
"name": "ABC",
"avgTimeTaken": 25.0
},
{
"time": "2017",
"name": "XYZ",
"avgTimeTaken": 40.0
},
{
"time": "2016",
"name": "ABC",
"avgTimeTaken": 25.0
},
{
"time": "2016",
"name": "XYZ",
"avgTimeTaken": 0.0
}
]
I'm thinking to perform iterations on the resultMap
and prepare the 2nd format. I'm trying to perform the iteration again on the resultMap
. Is there any other way to handle this?
Actually it's pretty interesting what you are trying to achieve. It's like you are trying to do some sort of logical padding
. The way I've done it is to use Collectors.collectingAndThen
. Once the result is there - I simply pad it with needed data.
Notice that I'm using Sets.difference
from guava
, but that can easily be put into a static method. Also there are additional operations performed.
So I assume your MyPojo
looks like this:
static class MyPojo {
private final String time;
private final String name;
private final String timetaken;
public MyPojo(String time, String name, String timetaken) {
super();
this.name = name;
this.time = time;
this.timetaken = timetaken;
}
public String getName() {
return name;
}
public String getTime() {
return time;
}
public String getTimetaken() {
return timetaken;
}
public static double getTimeTakenAsDouble(MyPojo pojo) {
return Double.parseDouble(pojo.getTimetaken());
}
}
And input data that I've checked against is :
List<MyPojo> myPojos = Arrays.asList(
new MyPojo("2017", "ABC", "30"),
new MyPojo("2017", "ABC", "20"),
new MyPojo("2016", "ABC", "25"),
new MyPojo("2017", "XYZ", "40"),
new MyPojo("2018", "RDF", "80"));
Here is the code that does what you want:
Set<String> distinctYears = myPojos.stream().map(MyPojo::getTime).collect(Collectors.toSet());
Map<String, Map<String, Double>> resultMap = myPojos.stream()
.collect(Collectors.groupingBy(MyPojo::getName,
Collectors.collectingAndThen(
Collectors.groupingBy(MyPojo::getTime,
Collectors.averagingDouble(MyPojo::getTimeTakenAsDouble)),
map -> {
Set<String> localYears = map.keySet();
SetView<String> diff = Sets.difference(distinctYears, localYears);
Map<String, Double> toReturn = new HashMap<>(localYears.size() + diff.size());
toReturn.putAll(map);
diff.stream().forEach(e -> toReturn.put(e, 0.0));
return toReturn;
}
)));
Result of that would be:
{ABC={2016=25.0, 2018=0.0, 2017=25.0},
RDF={2016=0.0, 2018=80.0, 2017=0.0},
XYZ={2016=0.0, 2018=0.0, 2017=40.0}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With