Home > other >  Natural sorting by alphanumeric properties in Java
Natural sorting by alphanumeric properties in Java

Time:12-07

Let's say I have this class below:

class Product{
  String name;
  ...
}

And then I make a list of products, List<Product> products. Let's say that there are 3 object with names=("A-Product-12", "A-Product-2", "A-Product-1").

I tried several ways, but it's always results to this order=("A-Product-1", "A-Product-12", "A-Product-2"), but I want it to be in this order=("A-Product-1", "A-Product-2", "A-Product-12")

I've tried using products.sort(), Arrays.sort(), stream().sorted() and using several comparators including Comparator.naturalOrder (which works against numeric only, but not with complex alphanumeric)

CodePudding user response:

You need to write your own custom Comparator that will compare only number from this String (or implement compareTo method in Product class). It can look like this:

List<String> strings = Arrays.asList("A-Product-12", "A-Product-2", "A-Product-1");

Collections.sort(strings, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        int n1 = Integer.parseInt(s1.split("-")[2]);  // Get numeric part of first string
        int n2 = Integer.parseInt(s2.split("-")[2]);  // Get numeric part of second string
        return Integer.compare(n1, n2);  // Compare the numeric parts of the strings
    }
});

System.out.println(strings);  // Output: [A-Product-1, A-Product-2, A-Product-12]

CodePudding user response:

Implementing the Comparable interface with the Product class allows us to sort Product objects directly.
public class Product implements Comparable<Product>

We then define how we want this class to be sorted by overriding the compareTo method. This is done in the Product class.

This is a clean way of implementing the sorting of products (a collection of Product objects). If we want to change the way they are sorted, we know exactly where to go to do this, and the changes will be implemented anywhere we have sorted Product objects. For example, let's say we want to add products starting with the letter B ("B-Product-3") and we want the products to be sorted first by this letter and then by the number at the end. We could do this with the following compareTo method in the Product class:

@Override
public int compareTo(Product product) {
    int thisProductNumber = Integer.parseInt(name.split("-")[2]);
    int compareToProductNumber = Integer.parseInt(product.name.split("-")[2]);
    String thisBaseProductName = name.split("-")[0]   "-"   name.split("-")[1];
    String compareToBaseProductName = product.name.split("-")[0]   "-"   product.name.split("-")[1];
    return thisBaseProductName.compareTo(compareToBaseProductName) == 0 ?
            thisProductNumber - compareToProductNumber : thisBaseProductName.compareTo(compareToBaseProductName);
}

Sorting is done with one line: Collections.sort(products);

If we have products with names=("A-Product-12", "A-Product-2", "B-Product-3", "A-Product-1")
Sorting them gives us ("A-Product-1", "A-Product-2", "A-Product-12", "B-Product-3")

CodePudding user response:

The reason for your result is you were going to sort strings. when going to sort this string list, read left to right by getting each product name and ordered. when consider A-Product-12 and A-Product-2, In 'A-Product-12', it has '1' before the '2'. so that it give priority to 12 than 2 in 'A-Product-2'. If you want to get your expected result you can use your list with map or you can apply logic to sort using last characters those are petitioned after the last character '-'.

CodePudding user response:

Better to create a custom Comparator as @Pekinek mentioned. Here's another version:


public class NumberComparator implements Comparator<Product> {
    
    @Override
    public int compare(Product p1, Product p2) {
        
        String p1str = p1.getName().replaceAll("[^0-9]", "");
        String p2str = p2.getName().replaceAll("[^0-9]", "");

        int p1number = Integer.parseInt(p1str);
        int p2number = Integer.parseInt(p2str);        

        return Integer.compare(p1number, p2number);
    }
}

In main class you can do

Collections.sort(list,new NumberComparator());
  • Related