How to sort Array if it contains number string and string data in JavaScript

eye-catchJavaScript/TypeScript
Sponsored links

Doesn’t Array.sort output as you expected? It’s because number is handled as string by default. If you want to sort the array on your needs, you need to write your own sorting logic.

Didn’t you get the following result when you tried to use Array.sort?

[27, 24, 100, 1, 2] -> 1,100,2,24,27
["27", "24", "100", "1", "002"] -> 002,1,100,24,27
["A", "AB", "ACC", "aaaa", "a", "ab", "acc"] -> A,AB,ACC,a,ab,acc
["27", "0xff", "100", "1", "002", "CCC", "AA", "aB"]
-> 002,0xff,1,100,27,AA,CCC,aB

Isn’t it what you want? Do you need to sort something like [3, 4, "A1", "B3", "AB5"]? Then, this post is for you.

Sponsored links

Sort number array

Let’s call sort method without parameter.

const nums = [27, 24, 100, 1, 2];
console.log(nums.sort().join(",")); // ASC: 1,100,2,24,27

The result might not be your desired output. If the compare function is not supplied to the sort method, it’s sorted in the following way.

If compareFn is not supplied, all non-undefined array elements are sorted by converting them to strings and comparing strings in UTF-16 code units order.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

The numbers are converted to string first. When it compares the first letters, 2 is bigger than 1. That’s why the result becomes like the above.

If you want to sort the array in numerical order, you have to specify the compare function.

console.log(nums.sort((a, b) => a - b).join(","));  // ASC:  1,2,24,27,100
console.log(nums.sort((a, b) => b - a).join(","));  // DESC: 100,27,24,2,1
console.log(nums.sort((a, b) => 0).join(","));  // Not sort: 100,27,24,2,1

Internal behavior is defined in the following way.

compareFn(a, b) return valuesort order
> 0sort a after b
< 0sort a before b
=== 0keep original order of a and b

Let’s make it simple.

FormulaResult
a – bASC
b – aDESC
Sponsored links

Sort string array

Normal string sorting is easy.

function ascending(a: string, b: string) {
    if (a > b) {
        return 1;
    }
    if (a < b) {
        return -1;
    }
    return 0;
}

function descending(a: string, b: string) {
    if (a < b) {
        return 1;
    }
    if (a > b) {
        return -1;
    }
    return 0;
}
const strs = [
    "AAA",
    "A",
    "AB",
    "ACC",
    "aaa",
    "aaaa",
    "a",
    "ab",
    "acc",
];
console.log(strs.sort().join(",")); // ASC: A,AAA,AB,ACC,a,aaa,aaaa,ab,acc
console.log(strs.sort(ascending).join(","));  // ASC:  A,AAA,AB,ACC,a,aaa,aaaa,ab,acc
console.log(strs.sort(descending).join(",")); // DESC: acc,ab,aaaa,aaa,a,ACC,AB,AAA,A

I guess no surprise.

Case insensitive

Use toLowerCase() or toUpperCase() if you want to ignore the difference between upper case and lower case.

function ascendingCaseIgnore(a: string, b: string) {
    const strA = a.toLowerCase();
    const strB = b.toLowerCase();
    if (strA > strB) {
        return 1;
    }
    if (strA < strB) {
        return -1;
    }
    return 0;
}
console.log(strs.sort(ascendingCaseIgnore).join(",")); // ASC: a,A,aaa,AAA,aaaa,ab,AB,acc,ACC

Number string array

If the array is a number string array, the result might not be your desired output.

const strNums = ["27", "24", "100", "1", "002"];
console.log(strNums.sort().join(","));  // 002,1,100,24,27

Sort numerical order

If you want to sort it in numerical order, you have to convert it to number.

console.log(strNums.sort((a, b) => {
    const numA = parseInt(a, 10);
    const numB = parseInt(b, 10);
    if (numA > numB) {
        return 1;
    }
    if (numA < numB) {
        return -1;
    }
    return 0;
}).join(","));  // 1,002,24,27,100

Take leading 0 into account

If you want to take leading 0 into account, you can implement it in the following way.

console.log(strNums.sort((a, b) => {
    const isLeadingZeroA = a.startsWith("0");
    const isLeadingZeroB = b.startsWith("0");
    if (isLeadingZeroA || isLeadingZeroB) {
        if (a > b) {
            return 1;
        }

        if (a < b) {
            return -1;
        }
        return 0;
    }

    const numA = parseInt(a, 10);
    const numB = parseInt(b, 10);
    if (numA > numB) {
        return 1;
    }
    if (numA < numB) {
        return -1;
    }
    return 0;
}).join(","));  // 002,1,24,27,100

Number string and non number string mixed

The basic sort order is as follows.

const strNumsMixed = ["27", "0xff", "100", "1", "002", "CCC", "AA", "aB"]
console.log(strNumsMixed.sort().join(",")); // 002,0xff,1,100,27,AA,CCC,aB

If you want to handle Hex as string but decimal number strings as numbers…

function sortStringFirst(a: string, b: string) {
    const regex = /[-+]??\d+/;
    const isNumA = regex.test(a);
    const isNumB = regex.test(b);

    if (isNumA && !isNumB) {
        return 1;
    }
    if (!isNumA && isNumB) {
        return -1;
    }
    if (isNumA && isNumB) {
        const numA = parseInt(a, 10);
        const numB = parseInt(b, 10);
        return numA - numB;
        // if (numA > numB) {
        //     return 1;
        // }
        // if (numA < numB) {
        //     return -1;
        // }
        // return 0;
    }
    return null;
}
function sortNumberFirst(a: string, b: string) {
    const regex = /^[-+]??\d+$/;
    const isNumA = regex.test(a);
    const isNumB = regex.test(b);

    if (isNumA && !isNumB) {
        return -1;
    }
    if (!isNumA && isNumB) {
        return 1;
    }
    if (isNumA && isNumB) {
        const numA = parseInt(a, 10);
        const numB = parseInt(b, 10);
        return numA - numB;
        // if (numA > numB) {
        //     return 1;
        // }
        // if (numA < numB) {
        //     return -1;
        // }
        // return 0;
    }
    return null;
}

console.log(strNumsMixed.sort((a, b) => {
    const result = sortStringFirst(a, b);
    if (result === null) {
        return ascending(a, b);
    }
    return result;
}).join(","));  // AA,CCC,aB,0xff,1,002,27,100

console.log(strNumsMixed.sort((a, b) => {
    const result = sortNumberFirst(a, b);
    if (result === null) {
        return ascending(a, b);
    }
    return result;
}).join(","));  // 1,002,27,100,0xff,AA,CCC,aB

It checks whether both values are number or not. If yes, compare them as number. If one of them is a string, do the following comparison depending on your needs.

// string first >>>
if (isNumA && !isNumB) {
    return 1;
}
if (!isNumA && isNumB) {
    return -1;
}
// <<<

// number first >>>
if (isNumA && !isNumB) {
    return -1;
}
if (!isNumA && isNumB) {
    return 1;
}
// <<<

If both are strings, do the comparison defined in the earlier section.

Number with Leading Alphabets and number string mixed array

Do you need to order something like this? [3, 4, "A1", "B3", "AB5"]

const extra = [
    "A1", "1", "A10", "A11", "A12", "5", "3", "10", "A2",
    "AB2", "A3", "A4", "B10", "B2", "F1", "F12", "F3",
];
console.log(extra.sort((a, b) => {
    const numeric = /^[-+]??\d+/;

    const isNumericA = numeric.test(a);
    const isNumericB = numeric.test(b);
    // e.g. compare 5 and 6
    if (isNumericA && isNumericB) {
        return parseInt(a, 10) - parseInt(b, 10);
    }

    // e.g. 5 and A2
    if (isNumericA && !isNumericB) {
        return -1;
    }

    // e.g. A2 and 6
    if (!isNumericA && isNumericB) {
        return 1;
    }

    const alphabets = /^[a-zA-Z]+/;
    // Alphabet + number: A1, B3...
    const aAlphabets = a.replace(/\d+/g, "");
    const bAlphabets = b.replace(/\d+/g, "");
    if (aAlphabets === bAlphabets) {
        // e.g. Compare AB10 and AB12
        const aNumber = a.replace(alphabets, "");
        const bNumber = b.replace(alphabets, "");
        // e.g. Compare 10 and 12 for AB10 and AB12
        const result = aNumber === bNumber ? 0 : parseInt(aNumber, 10) - parseInt(bNumber, 10);
        console.log(`A: ${a}, B: ${b}, result: ${result}`)
        return result;
    }
    // e.g. A12 and B12
    return aAlphabets > bAlphabets ? 1 : -1;
}).join(","));
// 1,3,5,10,A1,A2,A3,A4,A10,A11,A12,AB2,B2,B10,F1,F3,F12

What does the last part do? It removes number part and then compares the two alphabets. If it’s the same alphabets/string, it removes the alphabet part and compares the number part. The comment might help you understand the logic.

Related articles

Comments

Copied title and URL