-1

How can i sort strings contains numbers in java ? Example : abc12,abc2,xyz3,pqr23,pqr99,wer12 need to get abc2,abc12,pqr23,pqr99,wer12 i tried with comparator but can only compare that of numbers need to compare both strings and numbers . please help me. Thanks

J.K
  • 2,290
  • 1
  • 18
  • 29

3 Answers3

2

If you have always the format (lettersNumbers), you can use this regex (taken from here and slightly modified) :

private static final String PATTERN = "[^a-z0-9]+|(?<=[a-z])(?=[0-9])|(?<=[0-9])(?=[a-z])";
    public static void main(String[] args) {
        List<String> l = Arrays.asList("abc12","abc2","xyz3","pqr23","pqr99","wer12");
        Collections.sort(l, new Comparator<String>(){
            @Override
            public int compare(String arg0, String arg1) {
                String [] arr0 = arg0.split(PATTERN);
                String [] arr1 = arg1.split(PATTERN);
                int cmp = arr0[0].compareTo(arr1[0]);
                return cmp != 0 ? cmp : Integer.compare(Integer.parseInt(arr0[1]), Integer.parseInt(arr1[1]));
            }           
        });
        System.out.println(l);
    }

Output :

[abc2, abc12, pqr23, pqr99, wer12, xyz3]
Community
  • 1
  • 1
user2336315
  • 15,697
  • 10
  • 46
  • 64
1

EDIT

One way of doing it in a simplistic manner would be pad the numbers with preceeding zeroes. so, say if you have strings such as

abc1
abc10 
abc100

You parse your strings and pad the strings which have numbers occupy the same space. so your strings would become

abc001
abc010
abc100

Now, just sort them, and the natural ordering should sort them beautifully without a hassle.

This is all provided you have a knowledge of how many numbers will constitute the part of the String.

Also, you might want to output them in the fashion of abc001 instead of abc1 since that would help your sorting immensly.

Can you not parse and update your strings to be from say abc2 to abc02 If you do that, the sorting after that would just work fine. isnt it?

So, whereever there is just 1 number pad it before with a 0.

But, inspite of that it is still doable and an easier solution than the comparator in my opinion

Hrishikesh
  • 2,033
  • 1
  • 16
  • 26
1

Use natural sorting:

package org.cougaar.util;

import java.util.Comparator;

/**
 * A sorting comparator to sort strings numerically,
 * ie [1, 2, 10], as opposed to [1, 10, 2].
 */
public final class NaturalOrderComparator<T> implements  Comparator<T> {

    public static final Comparator<String> NUMERICAL_ORDER = new NaturalOrderComparator<String>(false);
    public static final Comparator<String> CASEINSENSITIVE_NUMERICAL_ORDER = new NaturalOrderComparator<String>(true);

    private final boolean caseInsensitive;

    private NaturalOrderComparator(boolean caseInsensitive) {
        this.caseInsensitive = caseInsensitive;
    }

    int compareRight(String a, String b) {
        int bias = 0;
        int ia = 0;
        int ib = 0;

        // The longest run of digits wins.  That aside, the greatest
        // value wins, but we can't know that it will until we've scanned
        // both numbers to know that they have the same magnitude, so we
        // remember it in BIAS.
        for (;; ia++, ib++) {
            char ca = charAt(a, ia);
            char cb = charAt(b, ib);

            if (!Character.isDigit(ca) && !Character.isDigit(cb)) {
                return bias;
            } else if (!Character.isDigit(ca)) {
                return -1;
            } else if (!Character.isDigit(cb)) {
                return +1;
            } else if (ca < cb) {
                if (bias == 0) {
                    bias = -1;
                }
            } else if (ca > cb) {
                if (bias == 0)
                    bias = +1;
            } else if (ca == 0 && cb == 0) {
                return bias;
            }
        }
    }

    public int compare(T o1, T o2) {
        String a = o1.toString();
        String b = o2.toString();

        int ia = 0, ib = 0;
        int nza = 0, nzb = 0;
        char ca, cb;
        int result;

        while (true) {
            // only count the number of zeroes leading the last number compared
            nza = nzb = 0;

            ca = charAt(a, ia);
            cb = charAt(b, ib);

            // skip over leading zeros
            while (ca == '0') {
                if (ca == '0') {
                    nza++;
                } else {
                    // only count consecutive zeroes
                    nza = 0;
                }

                // if the next character isn't a digit, then we've had a run of only zeros
                // we still need to treat this as a 0 for comparison purposes
                if (!Character.isDigit(charAt(a, ia+1)))
                    break;

                ca = charAt(a, ++ia);
            }

            while (cb == '0') {
                if (cb == '0') {
                    nzb++;
                } else {
                    // only count consecutive zeroes
                    nzb = 0;
                }

                // if the next character isn't a digit, then we've had a run of only zeros
                // we still need to treat this as a 0 for comparison purposes
                if (!Character.isDigit(charAt(b, ib+1)))
                    break;

                cb = charAt(b, ++ib);
            }

            // process run of digits
            if (Character.isDigit(ca) && Character.isDigit(cb)) {
                if ((result = compareRight(a.substring(ia), b
                        .substring(ib))) != 0) {
                    return result;
                }
            }

            if (ca == 0 && cb == 0) {
                // The strings compare the same.  Perhaps the caller
                // will want to call strcmp to break the tie.
                return nza - nzb;
            }

            if (ca < cb) {
                return -1;
            } else if (ca > cb) {
                return +1;
            }

            ++ia;
            ++ib;
        }
    }

    private char charAt(String s, int i) {
        if (i >= s.length()) {
            return 0;
        } else {
            return caseInsensitive ? Character.toUpperCase(s.charAt(i)) : s.charAt(i);
        }
    }


}
Abimaran Kugathasan
  • 31,165
  • 11
  • 75
  • 105
Alex K.
  • 3,294
  • 4
  • 29
  • 41