Intro
I will try sorting a specific formatted string values.
Before
'SAMPLE1-1-4-A',
'SAMPLE1-1-4-3',
'SAMPLE1-1-2-B',
'SAMPLE1-2-1',
'SAMPLE1-2-12-2',
'SAMPLE1-2-2-F',
'SAMPLE1-2-5-A',
'SAMPLE1-3-5',
'SAMPLE2-3-10',
'SAMPLE2-4-2-10',
'SAMPLE2-4-2-1',
'SAMPLE2-1-1-14',
'SAMPLE2-14-2-Z',
'SAMPLE2-1-10-C',
'SAMPLE2-1-4-1',
'SAMPLE2-1-4',
'SAMPLE2-2-4-A',
'SAMPLE2-23-2',
'SAMPLE2-4-2-2',
'SAMPLE2-10-2'
After
"SAMPLE1-1-2-B"
"SAMPLE1-1-4-3"
"SAMPLE1-1-4-A"
"SAMPLE1-2-1"
"SAMPLE1-2-2-F"
"SAMPLE1-2-5-A"
"SAMPLE1-2-12-2"
"SAMPLE2-1-4"
"SAMPLE2-1-4-1"
"SAMPLE2-1-10-C"
"SAMPLE2-2-4-A"
"SAMPLE2-3-10"
"SAMPLE2-4-2-1"
"SAMPLE2-4-2-2"
"SAMPLE2-4-2-10"
"SAMPLE2-4-B"
"SAMPLE2-10-2"
"SAMPLE2-14-2-Z"
"SAMPLE2-D-1-14"
"SAMPLE10-3-5"
localeCompare (Failed)
I sorted them with "localeCompare".
sortSample.ts
export class SortSample {
private originalTexts: readonly string[] = [
'SAMPLE1-1-4-A',
'SAMPLE1-1-4-3',
'SAMPLE1-1-2-B',
'SAMPLE1-2-1',
'SAMPLE1-2-12-2',
'SAMPLE1-2-2-F',
'SAMPLE1-2-5-A',
'SAMPLE10-3-5',
'SAMPLE2-3-10',
'SAMPLE2-4-2-10',
'SAMPLE2-4-2-1',
'SAMPLE2-D-1-14',
'SAMPLE2-14-2-Z',
'SAMPLE2-1-10-C',
'SAMPLE2-1-4-1',
'SAMPLE2-1-4',
'SAMPLE2-2-4-A',
'SAMPLE2-4-B',
'SAMPLE2-4-2-2',
'SAMPLE2-10-2'
];
public sort(): readonly string[] {
const copiedTexts = [...this.originalTexts];
return copiedTexts.sort((a, b) => a.localeCompare(b));
}
}
Result
0: "SAMPLE1-1-2-B"
1: "SAMPLE1-1-4-3"
2: "SAMPLE1-1-4-A"
3: "SAMPLE1-2-1"
4: "SAMPLE1-2-12-2"
5: "SAMPLE1-2-2-F"
6: "SAMPLE1-2-5-A"
7: "SAMPLE1-3-5"
8: "SAMPLE2-1-1-14"
9: "SAMPLE2-1-10-C"
10: "SAMPLE2-1-4"
11: "SAMPLE2-1-4-1"
12: "SAMPLE2-10-2"
13: "SAMPLE2-14-2-Z"
14: "SAMPLE2-2-4-A"
15: "SAMPLE2-23-2"
16: "SAMPLE2-3-10"
17: "SAMPLE2-4-2-1"
18: "SAMPLE2-4-2-10"
19: "SAMPLE2-4-2-2"
They were orderd like "SAMPLE2-4-2-1" -> "SAMPLE2-4-2-10" -> "SAMPLE2-4-2-2".
Numbers such as "4", "2", and "1" should be treated as "number" type instead of "string" type.
Split and parse to numbers
I Split and parse them to numbers.
sortSample.ts
export class SortSample {
private originalTexts: readonly string[] = [
...
];
public sort(): readonly string[] {
console.log(this.compareWithSplit);
const copiedTexts = [...this.originalTexts];
//return copiedTexts.sort((a, b) => a.localeCompare(b));
return copiedTexts.sort((a, b) => this.compareWithSplit(a, b))
}
private compareWithSplit(a: string|null|undefined, b: string|null|undefined): number {
if(a == null && b == null) {
return 0;
}
if(a == null) {
return 1;
}
if(b == null) {
return -1;
}
if(a.startsWith('SAMPLE') === false) {
return a.localeCompare(b);
}
const splittedA = a.replace('SAMPLE', '').split('-');
const splittedB = b.replace('SAMPLE', '').split('-');
let index = 0;
for(const sa of splittedA) {
if(index >= splittedB.length) {
return 1;
}
// to ignore compile error because the type is treated as string|undefined
const sb = splittedB[index] ?? '';
const na = parseInt(sa);
const nb = parseInt(sb);
if(isNaN(na)) {
// sa: [A-Z]
if(isNaN(nb)) {
// sb: [A-Z]
return sa.localeCompare(sb);
}
return 1;
}
if(isNaN(nb)) {
// sa: [0-9] sb: [A-Z]
return -1;
}
if((na - nb) !== 0) {
return (na - nb);
}
// if na === nb, move next values.
index += 1;
}
return (splittedA.length - splittedB.length);
}
}
Result
0: "SAMPLE1-1-2-B"
1: "SAMPLE1-1-4-3"
2: "SAMPLE1-1-4-A"
3: "SAMPLE1-2-1"
4: "SAMPLE1-2-2-F"
5: "SAMPLE1-2-5-A"
6: "SAMPLE1-2-12-2"
7: "SAMPLE2-1-4"
8: "SAMPLE2-1-4-1"
9: "SAMPLE2-1-10-C"
10: "SAMPLE2-2-4-A"
11: "SAMPLE2-3-10"
12: "SAMPLE2-4-2-1"
13: "SAMPLE2-4-2-2"
14: "SAMPLE2-4-2-10"
15: "SAMPLE2-4-B"
16: "SAMPLE2-10-2"
17: "SAMPLE2-14-2-Z"
18: "SAMPLE2-D-1-14"
19: "SAMPLE10-3-5"
Is there more simple way?
I find this code verbose, so I'm looking for a simpler way.
For example, because these texts are like "SAMPLE[0-9]-[0-9|A-Z]-[0-9|A-Z]" or "SAMPLE[0-9]-[0-9|A-Z]-[0-9|A-Z]-[0-9|A-Z]", so I can split them with regex.
...
private compareWithSplit(a: string|null|undefined, b: string|null|undefined): number {
...
const splittedA = a.match(/(([0-9]+)|([0-9|A-Z]+$))/g);
const splittedB = b.match(/(([0-9]+)|([0-9|A-Z]+$))/g);
if((splittedA == null || splittedA.length <= 0) ||
(splittedB == null || splittedB.length <= 0)) {
return a.localeCompare(b);
}
let index = 0;
for(const sa of splittedA) {
if(index >= splittedB.length) {
return 1;
}
// to ignore compile error because the type is treated as string|undefined
const sb = splittedB[index] ?? '';
const na = parseInt(sa);
const nb = parseInt(sb);
if(isNaN(na)) {
// sa: [A-Z]
if(isNaN(nb)) {
// sb: [A-Z]
return sa.localeCompare(sb);
}
return 1;
}
if(isNaN(nb)) {
// sa: [0-9] sb: [A-Z]
return -1;
}
if((na - nb) !== 0) {
return (na - nb);
}
// if na === nb, move next values.
index += 1;
}
return (splittedA.length - splittedB.length);
}
...
But I think it's not so efficient.
If I found more better way, I will write in this post.
Top comments (0)