混合字母/数字数组排序

93

我有一个混合数组,需要按字母表顺序和数字顺序排序

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

如何排序才能得到:

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

我已经尝试过

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

但那只是按字母顺序排序。这个问题可以使用纯JavaScript或jQuery来解决吗?


数值总是在字符串的末尾吗? - Orbling
2
可能是如何在JavaScript中对字符串进行排序的重复问题。 - feeela
18个回答

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

用法:

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))

输出结果:

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

你可能需要将'en'参数更改为适合你所在地区的语言或根据程序进行确定,但这对英文字符串有效。

localeCompare支持IE11、Chrome、Firefox、Edge和Safari 10。


在我看来,这应该是被接受的答案。小问题:第一行在 { numeric: true }) 后面包含一个尾随反引号。 - w3dot0
这里展示的内置字母数字比较虽然方便,但实际上非常慢。被接受的解决方案快了3倍,但有时会给出错误的答案。正如我在那里评论的那样,我的代码得到了正确的答案,并且再次快了5倍,因此它比这个解决方案快15倍。请参见https://alphanum.timkay.com/cmpalphanum.js?run=edit - timkay
@w3dot0:我同意你的观点,但是如果你了解时间的运作方式,一个在2017年5月26日给出的答案不可能神奇地成为一个被接受的答案,取代我在2010年12月2日接受的答案。而且看起来在2010年并不存在localeCompare()函数。 - undefined

111

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)
)


5
稍晚了一点,但是你不需要else块,因为如果aA === bA,第一个ifreturn - phatskat
2
@Noitidart 的偏好。这两者之间不应该有任何区别。 - epascarello
5
这是一个很好的答案,但非常需要注释。我花了一些时间才读懂它。 - zfrisch
2
@epascarello 我真的很感激当我找到它时 - 当你第一次看到它时,它有点令人困惑。它简洁明了,但没有主题名称,如果您不熟悉正则表达式、三元运算符或常规排序,理解起来有点困难。字母数字排序是一个相当普遍的问题,而提出这个问题的上升过程并不需要更多的数组基础知识,因此假设超出这个范围会让人们要求评论。这是一个好答案,也是你的权利,但描述可能会使每个感兴趣的人更容易理解。 - zfrisch
1
没有冒犯的意思,因为这确实帮了我。+1. - zfrisch
显示剩余16条评论

12
您可以使用 Intl.Collator,它比localeCompare具有更好的性能表现。请在此阅读相关信息所有浏览器都支持该函数。

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)


2
我在Node.js中尝试了对此进行基准测试,结果比localeCompare的答案快得多。我进行了1000次迭代测试,localeCompare花费了3.159秒,而在相同条件下,这个Intl.Collator只需200.178毫秒,非常令人印象深刻。 - PurplProto

11

我遇到了类似的情况,但是有字母和数字的混合并且需要先将所有数字排序,然后再排序字母数字混合的部分,因此:

A10
1
5
A9
2
B3
A2

需要成为:

1
2
5
A2
A9
A10
B3

我能够使用提供的算法,并稍微修改它,以实现这个目标:

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);

1
["a25b", "ab", "a37b"] 将会产生 [ "a25b", "ab", "a37b" ] 而不是 [ "a25b", "a37b", "ab" ] - 林果皞

9
使用 localeCompare() 方法是实现这个的一个简单方法,它是 JavaScript 的一种方法。https://www.w3schools.com/jsref/jsref_localecompare.asp 示例:
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);
};

预期行为:

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

3
这应该就是答案了。不错! - Donny V.
4
物品的顺序不正确!比如说,1000X 应该在 10X 之后。请注意调整。 - timkay

3
这可能可以做到:

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']))


3
JavaScript数组排序函数包含一个可选参数:比较函数(compare function)。您可以根据自己的需求设置这个比较函数。

arr.sort([compareFunction])

compareFunction(可选):指定定义排序顺序的函数。如果省略,则根据每个元素的字符串转换,按照 Unicode 码位点的值对数组进行排序。-MDN


3

我最近参与了一个与库存和货架位置相关的项目。数据需要按货架位置进行排序,而这些数据存储在对象数组中。

如果您想要处理此类数据的排序,并且您的数据存储在对象数组中,您可以这样做:

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));

你还可以轻松地按照其他关键字进行排序,例如在代码片段中展示的那样,使用点符号表示 itemquantity

3
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/


2

在epascarello接受的答案基础上,由于我无法对其进行评论,因此我希望补充说明。作为一个新手,当其中一个字符串没有数字时,原始答案将不起作用。例如,A和A10将不能按照顺序排序。因此,在这种情况下,您可能需要返回到正常排序。

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);`

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接