Home > database >  Custom Sort using Java 8
Custom Sort using Java 8

Time:02-12

Consider the dto class below -

public class RoleDto {

  private String name;

  RoleDto(String name) {
    this.name = name;
  }
}

Its instances would contain names like -

Business Practitioner - Expert
Developer - Expert
Developer - Master
Business Practitioner - Master
Business Practitioner
Developer
Developer - Professional
Business Practitioner - Professional 

I want to sort the instances such that the order is -

Business Practitioner - Expert
Business Practitioner - Master
Business Practitioner - Professional
Business Practitioner

Developer - Expert
Developer - Master
Developer - Professional
Developer

I have used the code below -

ArrayList<RoleDto> roles = new ArrayList<>();
roles.add(new RoleDto("Business Practitioner - Expert"));
roles.add(new RoleDto("Developer - Expert"));
roles.add(new RoleDto("Developer - Master"));
roles.add(new RoleDto("Business Practitioner - Master"));
roles.add(new RoleDto("Business Practitioner"));
roles.add(new RoleDto("Developer"));
roles.add(new RoleDto("Developer - Professional"));
roles.add(new RoleDto("Business Practitioner - Professional"));

List<RoleDto> sorted = roles.stream().sorted(comparing(r -> r.getName(),
    comparing((String s) -> !s.contains("-")))).collect(toList());

for(RoleDto r : sorted)
System.out.println(r.getName());

However the output is

Business Practitioner - Expert
Developer - Expert
Developer - Master
Business Practitioner - Master
Developer - Professional
Business Practitioner - Professional
Business Practitioner
Developer

Can someone please help me achieve the expected result

CodePudding user response:

I would suggest changing your RoleDto to have two attributes: String role and String level

Now the code for sorting List would look like:

Comparator<RoleDto> compareByRoleAndLevel = Comparator
                    .comparing(RoleDto::getRole)
                    .thenComparing(RoleDto::getLevel);
 
List<RoleDto> sortedRoles = roles.stream()
                .sorted(compareByRoleAndLevel)
                .collect(Collectors.toList());

CodePudding user response:

You do not actually compare the names, the compare just checks whether or not the names contain "-", which is why you are getting all that do (in random order) before all that don't (in random order). If you can change the Dto, see the answer of @Orr, otherwise you could do the name split/compare in place, like this:

    final List<RoleDto> sorted =
        roles.stream()
            .sorted(
                comparing(
                    RoleDto::getName,
                    comparing((String s) -> s.split(" - ")[0])
                        .reversed()
                        .thenComparing(s -> s.split(" - ").length)
                        .reversed()
                        .thenComparing(s -> s.split(" - ")[1])))
            .collect(toList());

The reversing may seem a little unintuitive, but effectively every reverse reverses all the previous comparisons, so the first comparison get reversed twice. To skip that, you could instead write:

    final List<RoleDto> sorted =
        roles.stream()
            .sorted(
                comparing(
                    RoleDto::getName,
                    comparing((String s) -> s.split(" - ")[0])
                        .thenComparing(s -> -s.split(" - ").length)
                        .thenComparing(s -> s.split(" - ")[1])))
            .collect(toList());

(notice the minus sign in the middle compare)

  • Related