Home > OS >  Sort by first column, after by second if first contains duplicates etc
Sort by first column, after by second if first contains duplicates etc

Time:11-26

I have a simple .txt file, which contains table, where columns are divided by tabulation. So, I need to read data from the file and write to another in a sorted way. I need firstly sort by first column. If it contains duplicates values, values from second column should be sort (only ones which are against duplicates in the first column) and so on. Numbers are higher than letters, numbers are in increasing order, letters are in alphabetical order. So, if I have file like this

enter image description here

after sorting I need to have this one

enter image description here

I've found this one How do I sort records in a text file using Java?, but is there effective way to implement remaining sorting logic, not only by first column?

CodePudding user response:

You can try the following algorithm.

  1. Create class TableLine with 4 String fields: rowOne, rowTwo, rowThree, rowFour (I suppose maximum row number is 4?).

  2. Read lines from file and put every line in TableLine object. You should have List<TableLine> as result.

  3. Create a comparator:

    Comparator<TableLine> comparator
    = Comparator.comparing(TableLine::getRowOne)
                .thenComparing(TableLine::getRowTwo)...
    

    etc for all rows you need to consider.

  4. Sort your List using above comparator.

  5. Write back to file

CodePudding user response:

So, actually you try to compare rows by their contents, that is, each row can be represented as an array of strings, where some strings should be compared as numbers.

So, custom comparator needs to be implemented like this according to the mentioned requirements:

  • numbers should be compared as numbers
  • strings should be compared alphabetically
  • numbers go before strings (therefore less than strings)
private static int customCompare(String[] s1, String[] s2) {
    int res = 0;
    for (int i = 0, n = Math.min(s1.length, s2.length); res == 0 && i < n; i  ) {
        Comparable e1 = convert(s1[i]);
        Comparable e2 = convert(s2[i]);
         
        if (e1 instanceof Double && e2 instanceof Double) {
            res = Double.compare((Double)e1, (Double)e2);
        } else if (e1 instanceof String && e2 instanceof String) {
            res = e1.compareTo(e2);
        } else {
            res = e1 instanceof Double ? -1 : 1;
        }
    }
    return res != 0 ? res : Integer.compare(s1.length, s2.length);
}

// utility method to check for numeric format and convert to Double if needed
private static Comparable convert(String s) {
    if (null == s || !s.matches("[- ]?(\\d |\\d*\\.\\d )")) {
        return s;
    }
    return Double.valueOf(s);
}

Test:

String[] data = {
    "-2.2\t2\t3\t4",
    "2.2\t12345q\t69\t-afq",
    "2.2\t12345q\t69\t-asdf",
    "-22\t1234234\tasdfasf\tasdgas",
    "-22\t-3\t4",
    "",
    "-1.1",
    " ",
    "qqqq\tq1.1",
    "qqqq\t0.1",
    "qqqq\t-1.1",
    "-22\t11\tabc"
};

Arrays.stream(data)
      .map(s -> s.split("\t")) // Stream<String[]> rows
      .sorted(MyClass::customCompare) // reference to customCompare method
      .map(arr -> String.join("\t", arr))
      .forEach(System.out::println);

Output:

-22 -3  4
-22 11  abc
-22 1234234 asdfasf asdgas
-2.2    2   3   4
-1.1
2.2 12345q  69  -afq
2.2 12345q  69  -asdf

 
qqqq    -1.1
qqqq    0.1
qqqq    q1.1
  • Related