Search code examples
javasortingjava-8nested-loops

"Partially" sorting list of POJO


I have a List of objects of the following class:

public class Foo {
    private Date date;
    private String name;
    private Long number;
}

This list is fetched from a database with order by date asc, number desc, but the part that need to be retained all the time is the ordering by date asc.

Example of the result (Dateformat = MM/dd/yyyy):

01/01/2016  Name1   928562
01/01/2016  Name2   910785
01/01/2016  Name3   811290
01/01/2016  Name4   811289
01/01/2016  Name5   5000000
02/01/2016  Name3   877702
02/01/2016  Name1   852960
02/01/2016  Name2   749640
02/01/2016  Name4   749500
02/01/2016  Name5   5000000

Now I want to order that list so that it results in:

01/01/2016  Name2   910785
01/01/2016  Name1   928562
01/01/2016  Name3   811290
01/01/2016  Name4   811289
01/01/2016  Name5   5000000
02/01/2016  Name2   749640
02/01/2016  Name1   852960
02/01/2016  Name3   877702
02/01/2016  Name4   749500
02/01/2016  Name5   5000000

As you can see, it is now sorted by date ascending and the name. The order for the names is stored in another List (NameSortingList):

Name2
Name1
Name3

Note that Name4 and Name5 are missing in the NameSortingList, can't be added to it and therefore should be added after everything that is ordered. Everything that comes after the ordered list can have any order.

If it makes it easier, everything that is not in the lsit can be merged into one Foo per unique date with name = "Other" which sums up the Numbers of all the elements in it. An example for a result like that:

01/01/2016  Name2   910785
01/01/2016  Name1   928562
01/01/2016  Name3   811290
01/01/2016  Other   5811289
02/01/2016  Name2   749640
02/01/2016  Name1   852960
02/01/2016  Name3   877702
02/01/2016  Other   5749500

My Current approach to this sorting is to extract all dates as unqiue values first, then build the NameSortingList and then iterate the data multipel times to add the data in the right ordering. The issue I have is

  1. It can miss entries if the name is not existing in the NameSortingList
  2. The performance is really really bad

data is a list of Foo as described at the very top:

List<String> sortedNames = data.stream().filter(e -> e.getDate().equals(getCurrentMonthDate()))
        .map(e -> e.getName()).collect(Collectors.toCollection(ArrayList<String>::new));

Set<Date> uniqueDates = data.stream().map(e -> e.getDate())
        .collect(Collectors.toCollection(LinkedHashSet<Date>::new));

List<Foo> sortedFoo= new ArrayList<Foo>();
for (Date d : uniqueDates) {
    for (String name : sortedNames) {
        for (Foo fr : data) {
            if (fr.Date().equals(d) && fr.getName().equals(name)) {
                sortedFoo.add(fr);
                break;
            }
        }
    }
}

How can I fix the 2 issues I described? Maybe there is even a stream solution to this which I couldn't wrap my head around?


If you have any questions, feel free to ask


Solution

  • If I understand correctly, you have a list from the database that is sorted by date asc, number desc by default due to the query. You now want to sort it on date asc, name desc where the name are not sorted alphabetically, but based on the order they're in in the nameSortingList (where the names not in this list will be sorted at the end)?

    If that is indeed the case, how about:

    myList.sort(Comparator.comparing(Foo::getDate)
                          .thenComparing(foo-> {
      int index = nameSortingList.indexOf(foo.getName());
      return i == -1 ? // If not found, it should be sorted as trailing instead of leading name
        Integer.MAX_VALUE
       : // Otherwise, sort it on the index in the nameSortingList:
        i;} ));
    

    EDIT: As correctly pointed out by @tobias_k in the comments. It might be best to first create a Map for your nameSortingList, where the names are the keys, and the index in the nameSortingList are the value. This will be better for performance, so you can change it to this instead:

    myList.sort(Comparator.comparing(Foo::getDate)
                          .thenComparing(foo-> nameSortingMap.getOrDefault(foo.getName(), Integer.MAX_VALUE));
    

    Although I doubt it will make that big of a difference for small lists.