Java 8: grouping by multiple fields and then sort based on value

5k Views Asked by At

I have a class called Info

public class Info {
    private String account;
    private String opportunity;
    private Integer value;
    private String desc;
}

I want to group a list of Info, by two fields(account and then opportunity) and then sort it based on value. i.e. List of info before grouping and sorting:

List<Info> infos = [(account = 1234, opportunity = abs, value = 4, desc= test),
(account = 1234, opportunity = abs, value = 5, desc = testing),
(account = 1234, opportunity = abss, value = 10, desc = vip),
(account = 123, opportunity = abss, value = 8, desc = vip),
(account = 12, opportunity = absddd, value = 4, desc = production),
(account = 12, opportunity = absooo, value = 2, desc = test)]

The result I expected after grouping and sorting,

Map<String, Map<String, List<Info>>> result = {
    1234 = {
        abss = [(account = 1234, opportunity = abss, value = 10, desc = vip)],
        abs = [(account = 1234, opportunity = abs, value = 5, desc = testing),  
               (account = 1234, opportunity = abs, value = 4, desc = test)]
    },
    123 = {
        abss = [(account = 123, opportunity = abss, value = 8, desc = vip)]
    },
    12 = {
        absddd = [(account = 12, opportunity = absddd, value = 4, desc = production)],
        absooo = [(account = 12, opportunity = absooo, value = 2, desc = test)]
    }
}

o/p is sorted based on value(10->(5+4)->8->4->2)

I have tried so far = infos.stream().collect(Collectors.groupingBy(Info::getAccount, Collectors.groupingBy(r -> r.getOpportunity(), Collectors.toList()))) but its sorting randomly.

1

There are 1 best solutions below

5
On BEST ANSWER

To sort base on the value of Info,

  1. Make the stream sorted, by comparing the value, such that grouping will execute in order.
  2. When calling groupingBy, Specify LinkedHashMap in mapFactory to preserve the insertion order .

Following program demonstrates how to implement.


import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Info {
    private String account;
    private String opportunity;
    private Integer value;
    private String desc;

    private Info(String account, String opportunity, Integer value, String desc) {
        super();
        this.account = account;
        this.opportunity = opportunity;
        this.value = value;
        this.desc = desc;
    }

    public static void main(String[] args) {
        List<Info> infos = new ArrayList<>();
        infos.add(new Info("12", "absddd", 4, "production"));
        infos.add(new Info("1234", "abss", 10, "vip"));
        infos.add(new Info("1234", "abs", 4, "test"));
        infos.add(new Info("1234", "abs", 5, "testing"));
        infos.add(new Info("123", "abss", 8, "vip"));
        infos.add(new Info("12", "absooo", 2, "test"));
        Map<String, Map<String, List<Info>>> sortedResult = infos.stream().sorted(Info::compareByValueDesc)
                .collect(Collectors.groupingBy(Info::getAccount, LinkedHashMap::new,
                        Collectors.groupingBy(r -> r.getOpportunity(), LinkedHashMap::new, Collectors.toList())));
        sortedResult.forEach((key, value) -> System.out.println(key + value.toString()));
    }

    public static int compareByValueDesc(Info other1, Info other2) {
        return -other1.value.compareTo(other2.value);
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getOpportunity() {
        return opportunity;
    }

    public void setOpportunity(String opportunity) {
        this.opportunity = opportunity;
    }

    public String toString() {
        return this.value.toString();
    }
}