Natural sorting, for digits use value (01 -> 1), order by leading zeros (1 > 01)

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2018-03-01 12:00:07 +01:00 committed by AndyScherzinger
parent 6b000595b1
commit c5d492cee9
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
2 changed files with 53 additions and 14 deletions

View file

@ -27,6 +27,7 @@ package third_parties.daveKoeller;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import java.io.File; import java.io.File;
import java.io.Serializable;
import java.text.Collator; import java.text.Collator;
import java.util.Comparator; import java.util.Comparator;
import java.util.Locale; import java.util.Locale;
@ -47,7 +48,7 @@ import java.util.Locale;
* https://github.com/nextcloud/server/blob/9a4253ef7c34f9dc71a6a9f7828a10df769f0c32/tests/lib/NaturalSortTest.php * https://github.com/nextcloud/server/blob/9a4253ef7c34f9dc71a6a9f7828a10df769f0c32/tests/lib/NaturalSortTest.php
* by Tobias Kaminsky * by Tobias Kaminsky
*/ */
public class AlphanumComparator<T> implements Comparator<T> { public class AlphanumComparator<T> implements Comparator<T>, Serializable {
private boolean isDigit(char ch) { private boolean isDigit(char ch) {
return ch >= 48 && ch <= 57; return ch >= 48 && ch <= 57;
} }
@ -120,17 +121,48 @@ public class AlphanumComparator<T> implements Comparator<T> {
// If both chunks contain numeric characters, sort them numerically // If both chunks contain numeric characters, sort them numerically
int result = 0; int result = 0;
if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
// Simple chunk comparison by length. // extract digits
int thisChunkLength = thisChunk.length(); int thisChunkZeroCount = 0;
result = thisChunkLength - thatChunk.length(); boolean zero = true;
// If equal, the first different number counts int c = 0;
if (result == 0) { while (c < (thisChunk.length()) && isDigit(thisChunk.charAt(c))) {
for (int i = 0; i < thisChunkLength; i++) { if (zero) {
result = thisChunk.charAt(i) - thatChunk.charAt(i); if (Character.getNumericValue(thisChunk.charAt(c)) == 0) {
if (result != 0) { thisChunkZeroCount++;
return result; } else {
zero = false;
} }
} }
c++;
}
int thisChunkValue = Integer.parseInt(thisChunk.substring(0, c));
int thatChunkZeroCount = 0;
c = 0;
zero = true;
while (c < (thatChunk.length()) && isDigit(thatChunk.charAt(c))) {
if (zero) {
if (Character.getNumericValue(thatChunk.charAt(c)) == 0) {
thatChunkZeroCount++;
} else {
zero = false;
}
}
c++;
}
int thatChunkValue = Integer.parseInt(thatChunk.substring(0, c));
result = Integer.compare(thisChunkValue, thatChunkValue);
if (result == 0) {
// value is equal, compare leading zeros
result = Integer.compare(thisChunkZeroCount, thatChunkZeroCount);
if (result != 0) {
return result;
}
} else {
return result;
} }
} else if (isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) { } else if (isSpecialChar(thisChunk.charAt(0)) && isSpecialChar(thatChunk.charAt(0))) {
for (int i = 0; i < thisChunk.length(); i++) { for (int i = 0; i < thisChunk.length(); i++) {

View file

@ -43,10 +43,10 @@ public class TestSorting {
@Test @Test
public void testSpecialChars() { public void testSpecialChars() {
String[] unsortedArray = {"11 - November", "Test 04", "Test 01", "Ôle", "Üüü", "01 - Januar", "[Test] Folder", String[] unsortedArray = {"Test 1", "11 - November", "Test 04", "Test 01", "Ôle", "Üüü", "01 - January", "[Test] Folder",
"z.[Test]", "z. Test"}; "z.[Test]", "z. Test"};
String[] sortedArray = {"[Test] Folder", "01 - Januar", "11 - November", "Ôle", "Test 01", "Test 04", "Üüü", String[] sortedArray = {"[Test] Folder", "01 - January", "11 - November", "Ôle", "Test 1", "Test 01", "Test 04", "Üüü",
"z. Test", "z.[Test]"}; "z. Test", "z.[Test]"};
assertTrue(sortAndTest(unsortedArray, sortedArray)); assertTrue(sortAndTest(unsortedArray, sortedArray));
@ -60,6 +60,14 @@ public class TestSorting {
assertTrue(sortAndTest(unsortedArray, sortedArray)); assertTrue(sortAndTest(unsortedArray, sortedArray));
} }
@Test
public void testTrailingZeros() {
String[] unsortedArray = {"T 3 abc", "T 2 abc", "T 03 abc", "T 01 abc", "T 0 abc", "T 02 abc", "T 1 abc", "T 001 abc", "T 000 abc", "T 00 abc"};
String[] sortedArray = {"T 0 abc", "T 00 abc", "T 000 abc", "T 1 abc", "T 01 abc", "T 001 abc", "T 2 abc", "T 02 abc", "T 3 abc", "T 03 abc"};
assertTrue(sortAndTest(unsortedArray, sortedArray));
}
@Test @Test
public void testNumbers() { public void testNumbers() {
String[] unsortedArray = {"124.txt", "abc1", "123.txt", "abc", "abc2", "def (2).txt", "ghi 10.txt", "abc12", String[] unsortedArray = {"124.txt", "abc1", "123.txt", "abc", "abc2", "def (2).txt", "ghi 10.txt", "abc12",
@ -99,7 +107,7 @@ public class TestSorting {
List<String> sortedList = Arrays.asList(sortedArray); List<String> sortedList = Arrays.asList(sortedArray);
Collections.sort(unsortedList, new AlphanumComparator()); Collections.sort(unsortedList, new AlphanumComparator<>());
for (int i = 0; i < sortedList.size(); i++) { for (int i = 0; i < sortedList.size(); i++) {
if (sortedList.get(i).compareTo(unsortedList.get(i)) != 0) { if (sortedList.get(i).compareTo(unsortedList.get(i)) != 0) {
@ -107,7 +115,6 @@ public class TestSorting {
System.out.println(" target: " + sortedList.toString()); System.out.println(" target: " + sortedList.toString());
System.out.println(" actual: " + unsortedList.toString()); System.out.println(" actual: " + unsortedList.toString());
return false; return false;
} }
} }