Vimalraj Selvam
Vimalraj Selvam

Reputation: 2245

Java 8 collect and change the format of the result

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?

Upvotes: 5

Views: 568

Answers (1)

Eugene
Eugene

Reputation: 120848

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}}

Upvotes: 4

Related Questions