Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 collect and change the format of the result

Tags:

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?

like image 780
Vimalraj Selvam Avatar asked May 17 '17 06:05

Vimalraj Selvam


1 Answers

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}}
like image 160
Eugene Avatar answered Sep 21 '22 10:09

Eugene