简体   繁体   中英

How to sort strings where a string contains alpha numeric characters like ROLL-10 > ROLL-2


I want to sort my custom object where it has two fields. 1) Pincode(Long) and 2) Roll(Alphanumeric). I wanted to sort Pincode as in natural ordering but when they are same, then I want to sort them by roll number in descending order.

I'm using Java 8 Comparators and I've the following code.
Model:

 public class AreaModel { public long pinCode; public String rollNo; public AreaModel(long pinCode, String rollNo) { super(); this.pinCode = pinCode; this.rollNo = rollNo; } public long getPinCode() { return pinCode; } public void setPinCode(long pinCode) { this.pinCode = pinCode; } public String getRollNo() { return rollNo; } public void setRollNo(String rollNo) { this.rollNo = rollNo; } @Override public String toString() { return "["+pinCode+" "+rollNo+"]"; } }

Main:

 import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; import com.javainuse.model.AreaModel; public class MainClass { public static void main(String[] args) { List<AreaModel> list = new ArrayList<AreaModel>(); list.add(new AreaModel(535005, "2")); list.add(new AreaModel(535006, "100")); list.add(new AreaModel(535007, "30")); list.add(new AreaModel(535005, "ROLE-45")); list.add(new AreaModel(535005, "ROLE-10")); list.add(new AreaModel(535005,"13")); list.add(new AreaModel(535005,"70")); Function<AreaModel, Long> retentionCodeSequence = AreaModel::getPinCode; Function<AreaModel, String> retentionDuration = AreaModel::getRollNo; // sort area by pincode, then by role Comparator<AreaModel> lastThenFirst = Comparator.comparing(retentionCodeSequence).thenComparing(retentionDuration,Comparator.reverseOrder()); list= list.stream().sorted(lastThenFirst).collect(Collectors.toList()); System.out.println("***"); System.out.println(list); } }

With that, the output what I'm getting is this


[[535005 ROLE-45], [535005 ROLE-10], [535005 70], [535005 2], [535005 13], [535006 100], [535007 30]]

And what I'm expecting is the following
[[535005 70], [535005 ROLE-45], [535005 13], [535005 ROLE-10], [535005 2], [535006 100], [535007 30]]

As you can see, even though pincodes have been sorted, roles haven't. And a role can contain whole number as a string or a number which is suffixed to a text. How do I resolve it?

Edit: updated question with possible values in the string

Assuming that all the valid values of the property rollNo have the format "ROLE-XX" , ie the prefix is the same and digits on the right are separated with a dash, you can use the following comparator , which makes use of the digit part of a rollNo :

Comparator<AreaModel> lastThenFirst =
    Comparator.comparing(AreaModel::getPinCode)
        .thenComparing(
            Comparator.<AreaModel>comparingInt(area -> Integer.parseInt(area.getRollNo().split("-")[1]))
                .reversed()
        );

As @g00se mentioned, the integer value in role should be parsed and used in the comparator.

Also, ToLongFunction could be used to handle pinCode and default List::sort should be used to sort current list.

ToLongFunction<AreaModel> pinCode = AreaModel::getPinCode;
ToIntFunction<AreaModel> roleToInt = am -> Integer.parseInt(am.getRollNo().replaceAll("\\D", ""));
Comparator<AreaModel> byRoleDesc = Comparator.comparingInt(roleToInt).reversed();

// sort area by pincode, then by role
Comparator<AreaModel> byPinAndReversedRole = Comparator
        .comparingLong(pinCode)
        .thenComparing(byRoleDesc);

list.sort(byPinAndReversedRole);
// -> [[535005 70], [535005 ROLE-45], [535005 13], [535005 ROLE-10], [535005 2], [535006 100], [535007 30]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM