Sort mixed alpha/numeric array

I have a mixed array that I need to sort by alphabet and then by digit

[A1, A10, A11, A12, A2, A3, A4, B10, B2, F1, F12, F3]

How do I sort it to be:

[A1, A2, A3, A4, A10, A11, A12, B2, B10, F1, F3, F12]

I have tried

arr.sort(function(a,b) {return a - b});

but that only sorts it alphabetically. Can this be done with either straight JavaScript or jQuery?

Answers:

Answer
const sortAlphaNum = (a, b) => a.localeCompare(b, 'en', { numeric: true })`

Usage:

const sortAlphaNum = (a, b) => a.localeCompare(b, 'en', { numeric: true })
console.log(['A1', 'A10', 'A11', 'A12', 'A2', 'A3', 'A4', 'B10', 'B2', 'F1', 'F12', 'F3'].sort(sortAlphaNum))

Gives:

["A1", "A2", "A3", "A4", "A10", "A11", "A12", "B2", "B10", "F1", "F3", "F12"]

You may have to change the 'en' argument to your locale or determine programatically but this works for english strings.

localeCompare is supported by IE11, Chrome, Firefox, Edge and Safari 10.

Answer
var a1 =["A1", "A10", "A11", "A12", "A2", "A3", "A4", "B10", "B2", "F1", "F12", "F3"];

var a2 = a1.sort(function(a,b){
    var charPart = [a.substring(0,1), b.substring(0,1)],
        numPart = [a.substring(1)*1, b.substring(1)*1];

    if(charPart[0] < charPart[1]) return -1;
    else if(charPart[0] > charPart[1]) return 1;
    else{ //(charPart[0] == charPart[1]){
        if(numPart[0] < numPart[1]) return -1;
        else if(numPart[0] > numPart[1]) return 1;
        return 0;
    }
});

$('#r').html(a2.toString())

http://jsfiddle.net/8fRsD/

Answer

This could do it:

function parseItem (item) {
  const [, stringPart = '', numberPart = 0] = /(^[a-zA-Z]*)(\d*)$/.exec(item) || [];
  return [stringPart, numberPart];
}

function sort (array) {
  return array.sort((a, b) => {
    const [stringA, numberA] = parseItem(a);
    const [stringB, numberB] = parseItem(b);
    const comparison = stringA.localeCompare(stringB);
    return comparison === 0 ? Number(numberA) - Number(numberB) : comparison;
  });
}

console.log(sort(['A1', 'A10', 'A11', 'A12', 'A2', 'A3', 'A4', 'B10', 'B2', 'F1', 'F12', 'F3']))
console.log(sort(['a25b', 'ab', 'a37b']))

Answer

Only problem with the above given solution was that the logic failed when numeric data was same & alphabets varied e.g. 28AB, 28PQR, 28HBC. Here is the modified code.

var reA = /[^a-zA-Z]/g;
    var reN = /[^0-9]/g;
    var AInt = parseInt(a, 10);
    var BInt = parseInt(b, 10);
    if(isNaN(AInt) && isNaN(BInt)){
        var aA = a.replace(reA, "");
        var bA = b.replace(reA, "");
        if(aA === bA) {
            var aN = parseInt(a.replace(reN, ""), 10);
            var bN = parseInt(b.replace(reN, ""), 10);
            alert("in if "+aN+" : "+bN);
            return aN === bN ? 0 : aN > bN ? 1 : -1;
        } else {
            return aA > bA ? 1 : -1;
        }
    }else if(isNaN(AInt)){//A is not an Int
        return 1;//to make alphanumeric sort first return 1 here
    }else if(isNaN(BInt)){//B is not an Int
        return -1;//to make alphanumeric sort first return -1 here
    }else if(AInt == BInt) {
        var aA = a.replace(reA, "");
        var bA = b.replace(reA, "");
        return aA > bA ? 1 : -1;
    }
    else {
        return AInt > BInt ? 1 : -1;
    }
Answer

Adding to the accepted answer from epascarello, since I cannot comment on it. I'm still a noob here. When one of the strinngs doesn't have a number the original answer will not work. For example A and A10 will not be sorted in that order. Hence you might wamnt to jump back to normal sort in that case.

var reA = /[^a-zA-Z]/g;
var reN = /[^0-9]/g;
function sortAlphaNum(a,b) {
    var aA = a.replace(reA, "");
    var bA = b.replace(reA, "");
    if(aA === bA) {
      var aN = parseInt(a.replace(reN, ""), 10);
      var bN = parseInt(b.replace(reN, ""), 10);
      if(isNaN(bN) || isNaN(bN)){
        return  a > b ? 1 : -1;
      }
      return aN === bN ? 0 : aN > bN ? 1 : -1;
    } else {
     return aA > bA ? 1 : -1;
    }
 }
 ["A1", "A10", "A11", "A12", "A2", "A3", "A4", "B10", "B2", "F1", "F12","F3"].sort(sortAlphaNum);`
Answer

I have solved the above sorting problem with below script

arrVals.sort(function(a, b){
    //return b.text - a.text;
    var AInt = parseInt(a.text, 10);
    var BInt = parseInt(b.text, 10);

    if ($.isNumeric(a.text) == false && $.isNumeric(b.text) == false) {
        var aA = a.text
        var bA = b.text;
        return aA > bA ? 1 : -1;
    } else if ($.isNumeric(a.text) == false) {  // A is not an Int
        return 1;    // to make alphanumeric sort first return -1 here
    } else if ($.isNumeric(b.text) == false) {  // B is not an Int
        return -1;   // to make alphanumeric sort first return 1 here
    } else {
        return AInt < BInt ? 1 : -1;
    }
});

This works fine for a well mixed array.:)

Thank you.

Answer

Here is an ES6 Typescript upgrade to this answer.

export function SortAlphaNum(a: string, b: string) {
const reA = /[^a-zA-Z]/g;
const reN = /[^0-9]/g;
const aA = a.replace(reA, "");
const bA = b.replace(reA, "");
if (aA === bA) {
    const aN = parseInt(a.replace(reN, ""), 10);
    const bN = parseInt(b.replace(reN, ""), 10);
    return aN === bN ? 0 : aN > bN ? 1 : -1;
} else {
    return aA > bA ? 1 : -1;
}

}

Answer

var reA = /[^a-zA-Z]/g;
var reN = /[^0-9]/g;

function sortAlphaNum(a, b) {
  var aA = a.replace(reA, "");
  var bA = b.replace(reA, "");
  if (aA === bA) {
    var aN = parseInt(a.replace(reN, ""), 10);
    var bN = parseInt(b.replace(reN, ""), 10);
    return aN === bN ? 0 : aN > bN ? 1 : -1;
  } else {
    return aA > bA ? 1 : -1;
  }
}
console.log(
["A1", "A10", "A11", "A12", "A2", "A3", "A4", "B10", "B2", "F1", "F12", "F3"].sort(sortAlphaNum)
)

Answer

I had a similar situation, but, had a mix of alphanumeric & numeric and needed to sort all numeric first followed by alphanumeric, so:

A10
1
5
A9
2
B3
A2

needed to become:

1
2
5
A2
A9
A10
B3

I was able to use the supplied algorithm and hack a bit more onto it to accomplish this:

var reA = /[^a-zA-Z]/g;
var reN = /[^0-9]/g;
function sortAlphaNum(a,b) {
    var AInt = parseInt(a, 10);
    var BInt = parseInt(b, 10);

    if(isNaN(AInt) && isNaN(BInt)){
        var aA = a.replace(reA, "");
        var bA = b.replace(reA, "");
        if(aA === bA) {
            var aN = parseInt(a.replace(reN, ""), 10);
            var bN = parseInt(b.replace(reN, ""), 10);
            return aN === bN ? 0 : aN > bN ? 1 : -1;
        } else {
            return aA > bA ? 1 : -1;
        }
    }else if(isNaN(AInt)){//A is not an Int
        return 1;//to make alphanumeric sort first return -1 here
    }else if(isNaN(BInt)){//B is not an Int
        return -1;//to make alphanumeric sort first return 1 here
    }else{
        return AInt > BInt ? 1 : -1;
    }
}
var newlist = ["A1", 1, "A10", "A11", "A12", 5, 3, 10, 2, "A2", "A3", "A4", "B10", "B2", "F1", "F12", "F3"].sort(sortAlphaNum);
Answer

A simple way to do this is use the localeCompare() method of JavaScript https://www.w3schools.com/jsref/jsref_localecompare.asp

Example:

export const sortAlphaNumeric = (a, b) => {
    // convert to strings and force lowercase
    a = typeof a === 'string' ? a.toLowerCase() : a.toString();
    b = typeof b === 'string' ? b.toLowerCase() : b.toString();

    return a.localeCompare(b);
};

Expected behavior:

1000X Radonius Maximus
10X Radonius
200X Radonius
20X Radonius
20X Radonius Prime
30X Radonius
40X Radonius
Allegia 50 Clasteron
Allegia 500 Clasteron
Allegia 50B Clasteron
Allegia 51 Clasteron
Allegia 6R Clasteron
Alpha 100
Alpha 2
Alpha 200
Alpha 2A
Alpha 2A-8000
Alpha 2A-900
Callisto Morphamax
Callisto Morphamax 500
Callisto Morphamax 5000
Callisto Morphamax 600
Callisto Morphamax 6000 SE
Callisto Morphamax 6000 SE2
Callisto Morphamax 700
Callisto Morphamax 7000
Xiph Xlater 10000
Xiph Xlater 2000
Xiph Xlater 300
Xiph Xlater 40
Xiph Xlater 5
Xiph Xlater 50
Xiph Xlater 500
Xiph Xlater 5000
Xiph Xlater 58
Answer

You can use Intl.Collator

It has performance benefits over localeCompare Read here

Browser comparability ( All the browser supports it )

let arr = ["A1", "A10", "A11", "A12", "A2", "A3", "A4", "B10", "B2", "F1", "F12", "F3"]

let op = arr.sort(new Intl.Collator('en',{numeric:true, sensitivity:'accent'}).compare)

console.log(op)

Answer
alphaNumericCompare(a, b) {

    let ax = [], bx = [];

    a.replace(/(\d+)|(\D+)/g, function (_, $1, $2) { ax.push([$1 || Infinity, $2 || '']) });
    b.replace(/(\d+)|(\D+)/g, function (_, $1, $2) { bx.push([$1 || Infinity, $2 || '']) });

    while (ax.length && bx.length) {
       let an = ax.shift();
       let bn = bx.shift();
       let nn = (an[0] - bn[0]) || an[1].localeCompare(bn[1]);
       if (nn) {
         return nn;
       }
     }
     return ax.length - bx.length;
}
Answer

This has worked for me and it's a bit more compact.

const reg = /[0-9]+/g;

array.sort((a, b) => {
     let v0 = a.replace(reg, v => v.padStart(10, '0'));
     let v1 = b.replace(reg, v => v.padStart(10, '0'));
     return v0.localeCompare(v1);
});
Answer
function sortAlphaNum(a, b) {
    var smlla = a.toLowerCase();
    var smllb = b.toLowerCase();
    var result = smlla > smllb ? 1 : -1;
    return result;
}
Answer

I recently worked on a project involving inventory and bin locations. The data needed to be sorted by bin location and was in an array of objects.

For anyone wanting to handle the sorting of this type of data, and your data is in an array of objects, you can do this:

const myArray = [
    { location: 'B3',   item: 'A', quantity: 25 },
    { location: 'A11',  item: 'B', quantity: 5 },
    { location: 'A6',   item: 'C', quantity: 245 },
    { location: 'A9',   item: 'D', quantity: 15 },
    { location: 'B1',   item: 'E', quantity: 65 },
    { location: 'SHOP', item: 'F', quantity: 42 },
    { location: 'A7',   item: 'G', quantity: 57 },
    { location: 'A3',   item: 'H', quantity: 324 },
    { location: 'B5',   item: 'I', quantity: 4 },
    { location: 'A5',   item: 'J', quantity: 58 },
    { location: 'B2',   item: 'K', quantity: 45 },
    { location: 'A10',  item: 'L', quantity: 29 },
    { location: 'A4',   item: 'M', quantity: 11 },
    { location: 'B4',   item: 'N', quantity: 47 },
    { location: 'A1',   item: 'O', quantity: 55 },
    { location: 'A8',   item: 'P', quantity: 842 },
    { location: 'A2',   item: 'Q', quantity: 67 }
];

const sortArray = (sourceArray) => {
    const sortByLocation = (a, b) => a.location.localeCompare(b.location, 'en', { numeric: true });
    //Notice that I specify location here ^^       and here        ^^ using dot notation
    return sourceArray.sort(sortByLocation);
};


console.log('unsorted:', myArray);

console.log('sorted by location:', sortArray(myArray));

You can easily sort by any of the other keys as well. In this case, item or quantity using dot notation as shown in the snippet.

Answer

Here's a version (based on the answer of @SunnyPenguin & @Code Maniac) that is in TypeScript as a library function. Variable names updated & comments added for clarity.

// Sorts strings with numbers by keeping the numbers in ascending order
export const sortAlphaNum: Function = (a: string, b: string, locale: string): number => {
  const letters: RegExp = /[^a-zA-Z]/g;
  const lettersOfA: string = a.replace(letters, '');
  const lettersOfB: string = b.replace(letters, '');

  if (lettersOfA === lettersOfB) {
    const numbers: RegExp = /[^0-9]/g;
    const numbersOfA: number = parseInt(a.replace(numbers, ''), 10);
    const numbersOfB: number = parseInt(b.replace(numbers, ''), 10);

    if (isNaN(numbersOfA) || isNaN(numbersOfB)) {
      // One is not a number - comparing letters only
      return new Intl.Collator(locale, { sensitivity: 'accent' }).compare(a, b);
    }
    // Both have numbers - compare the numerical parts
    return numbersOfA === numbersOfB ? 0 : numbersOfA > numbersOfB ? 1 : -1;
  } else {
    // Letter parts are different - comparing letters only
    return new Intl.Collator(locale, { sensitivity: 'accent' }).compare(lettersOfA, lettersOfB);
  }
};

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.