简体   繁体   中英

Sorting array with delimited values

I have an array with values of format 'int.int.int'

String[] tab = [ '100.2.0' , '5.7.4' , '7.6.1' , '5.6.4' ]

I want so sort this array by first number, then result of first sorting sort by second number still keeping array sorted by first value and the same thing with third number. Expected result:

[ '5.6.4' , '5.7.4' , '7.6.1' , '100.2.0' ]

The simplest solution is to create auxiliary arrays and use for loops, but I am wondering if it is possible to do it simpler. I've also tried to use groovy sorting

tab.sort { it -~ /\\./ }

where I've removed delimiters and sorted elements like they were integers, but it didn't work for example for such a values

[ '2.12.1' , '10.5.2' , '5.2.3' ]

The output was

[ '5.2.3' , '10.5.2' , '2.12.1' ]

Any ideas how to sort it in simple way, not by creating new arrays and iterating 3 times by original array?

Write a custom Comparator that split() s around . That will give you a String[] . Turn the first values of the two arrays into ints and compare them. If they're equal, turn the second two values into ints and compare, etc. If all three pairs of ints are equal, then the values are equal.

You could do this:

tab.sort { a, b -> [ a, b ]*.tokenize( '.' )*.
                           collect { it.toInteger() }
                           .transpose()
                           .findResult { x, y -> x <=> y ?: null } }

Or (arguably neater)

tab.sort { a, b ->
    [ a, b ]*.tokenize( '.' )
             .transpose()
             .findResult { x, y ->
                  x.toInteger() <=> y.toInteger() ?: null
              }
}

Or, if version numbers can be different numbers of integers (ie: 1.6 , 1.7.43.3 ):

tab.sort { a, b ->
    [ a, b ]*.tokenize( '.' ).with { u, v ->
        [ u, v ].transpose().findResult { x, y ->
             x.toInteger() <=> y.toInteger() ?: null
        } ?: u.size() <=> v.size()
    }
}

Or use a custom comparator ;-)

Use the Arrays.sort(....) method, and give it a Comparator that has the following compare(String a, String b) method:

public int compare (String a, String b) {
    String[] avals = a.split("\\.");
    String[] bvals = b.split("\\.");
    for (int i = 0; i < avals.length; a++) {
        int diff = Integer.parseInt(bvals[i]) - Integer.parseInt(avals[i]);
        if (diff != 0) {
            return diff;
        }
    }
    return 0;
}

If you want to have as little temporary object creation as possible you have to implement the parsing inside the comparator yourself.

public static final class XComparator implements Comparator<String>
{
  public static final Comparator<String> INSTANCE = new XComparator();
  private XComparator(){}

  public int compare(String o1, String o2)
  {
    final int l1=o1.length(), l2=o2.length();
    for(int p1=0, p2=0;; p1++, p2++)
    {
      int v1=0, v2=0;
      for(;p1<l1 && o1.charAt(p1)!='.';p1++) v1=nextChar(v1, o1.charAt(p1));
      for(;p2<l2 && o2.charAt(p2)!='.';p2++) v2=nextChar(v2, o2.charAt(p2));
      if(v1<v2) return -1; else if(v1>v2) return +1;
      if(p1==l1) return p2==l2? 0: -1;
      else if(p2==l2) return +1;
    }
  }
  private int nextChar(int v, char ch)
  {
    if(ch<'0'||ch>'9') throw new IllegalArgumentException();
    return v*10+ch-'0';
  }
}

Then you can say, for example:

String[] tab = { "100.2.0" , "5.7.4" , "7.6.1" , "5.6.4", "5", "3.4", "7.6" };
System.out.println(Arrays.toString(tab));
Arrays.sort(tab, XComparator.INSTANCE);
System.out.println(Arrays.toString(tab));

However for large datasets it can be still the case that converting to a pre-parsed representation before sorting (and converting back afterwards) is faster than on-the-fly parsing inside the comparator.

The idea is to convert each string to tuple of integers (let's name it NumberInfo , for example), ie "100.2.0" => { 100, 2, 0 }, because the integer representation is useful to perform comparison (comparison is used to sort items).

So, after conversion of List<String> to List<NumberInfo> , the List<NumberInfo> can be sorted.

"Tuple":

class NumberInfo {
    private int a;
    private int b;
    private int c;

    private NumberInfo(int a, int b, int c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    int getA() {
        return a;
    }

    int getB() {
        return b;
    }

    int getC() {
        return c;
    }

    public static NumberInfo fromString(String s) {
        String[] itemComponents = s.split("[.]");
        if (itemComponents.length != 3) {
            throw new IllegalArgumentException("Three comma delimited components expected");
        }

        int a = Integer.parseInt(itemComponents[0]);
        int b = Integer.parseInt(itemComponents[1]);
        int c = Integer.parseInt(itemComponents[2]);

        NumberInfo numberInfo = new NumberInfo(a, b, c);
        return numberInfo;
    }

    @Override
    public String toString() {
        return a + "." + b + "." + c;
    }
}

Tuple comparator:

class NumberInfoComparator implements Comparator<NumberInfo> {
    @Override
    public int compare(NumberInfo o1, NumberInfo o2) {
        int result = Integer.compare(o1.getA(), o2.getA());
        if (result != 0)
            return result;

        result = Integer.compare(o1.getB(), o2.getB());
        if (result != 0) {
            return result;
        }

        result = Integer.compare(o1.getC(), o2.getC());
        return result;
    }
}

Main method:

public static void main(String[] args) {
    String[] tab = { "100.2.0" , "5.7.4" , "7.6.1" , "5.6.4" };

    ArrayList<NumberInfo> numberInfoList = new ArrayList<NumberInfo>();
    for (String item : tab) {
        NumberInfo numberInfo = NumberInfo.fromString(item);
        numberInfoList.add(numberInfo);
    }

    NumberInfoComparator numberInfoComparator = new NumberInfoComparator();
    Collections.sort(numberInfoList, numberInfoComparator);

    for (NumberInfo numberInfo : numberInfoList) {
        System.out.println(numberInfo);
    }
}

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