排序数组元素(带数字的字符串),自然排序

IT技术 javascript jquery sorting
2021-01-17 03:37:53

我有一个像这样的数组;

["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"]

并且需要对其进行排序,使其看起来像;

["IL0 Foo", "IL3 Bob says hello", "IL10 Baz", "PI0 Bar"]

我尝试过排序功能;

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

但这给出了命令

["IL0 Foo", "IL10 Baz", "IL3 Bob says hello", "PI0 Bar"]

我试图想出一个可以工作但无法理解的正则表达式。
如果有帮助,格式将始终是 2 个字母、x 数量的数字,然后是任意数量的字符。

6个回答

这称为“自然排序”,可以像这样在 JS 中实现:

function naturalCompare(a, b) {
    var 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) {
        var an = ax.shift();
        var bn = bx.shift();
        var nn = (an[0] - bn[0]) || an[1].localeCompare(bn[1]);
        if(nn) return nn;
    }

    return ax.length - bx.length;
}

/////////////////////////

test = [
    "img12.png",
    "img10.png",
    "img2.png",
    "img1.png",
    "img101.png",
    "img101a.png",
    "abc10.jpg",
    "abc10",
    "abc2.jpg",
    "20.jpg",
    "20",
    "abc",
    "abc2",
    ""
];

test.sort(naturalCompare)
document.write("<pre>" + JSON.stringify(test,0,3));

要以相反的顺序排序,只需交换参数:

test.sort(function(a, b) { return naturalCompare(b, a) })

或者干脆

test = test.sort(naturalCompare).reverse();
感谢乔治的精彩回答!
2021-03-23 03:37:53
有人可以详细解释一下正则表达式替换 / Infinity 发生了什么吗?那么带有移位机制的 while 循环呢?我不完全明白那里发生了什么
2021-03-27 03:37:53
任何人都可以帮助如何实现数组的升序和降序排序?
2021-03-31 03:37:53
似乎更喜欢以字母字符开头的字符串而不是数字字符。通过更换固定$1 || 0typeof($1) == 'undefined' ? Infinity : $1
2021-04-04 03:37:53
请注意.reverse 将再次迭代数组。
2021-04-08 03:37:53

你可以使用String#localeCompareoptions

灵敏度

字符串中的哪些差异应导致非零结果值。可能的值为:

  • "base": 只有基本字母不同的字符串比较为不相等。示例:a ≠ ba = áa = A
  • "accent":只有在基本字母或重音和其他变音符号方面不同的字符串比较为不相等。示例:a ≠ ba ≠ áa = A
  • "case": 只有在基本字母或大小写不同的字符串比较为不相等。示例:a ≠ ba = áa ≠ A
  • "variant": 基本字母、重音和其他变音符号不同或大小写不同的字符串比较为不相等。也可以考虑其他差异。示例:a ≠ ba ≠ áa ≠ A

使用“排序”的默认值是“变体”;它取决于使用“搜索”的语言环境。

数字

是否应使用数字排序规则,例如“1”<“2”<“10”。可能的值为truefalse默认为false. 这个选项可以通过 options 属性或通过 Unicode 扩展键设置;如果两者都提供,则options属性优先。不需要实现来支持此属性。

var array = ["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"];

array.sort(function (a,b) {
    return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
});

console.log(array);

var re = /([a-z]+)(\d+)(.+)/i;
var arr = ["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"];
var order = arr.sort( function(a,b){
    var ma = a.match(re),
        mb = b.match(re),
        a_str = ma[1],
        b_str = mb[1],
        a_num = parseInt(ma[2],10),
        b_num = parseInt(mb[2],10),
        a_rem = ma[3],
        b_rem = mb[3];
    return a_str > b_str ? 1 : a_str < b_str ? -1 : a_num > b_num ? 1 : a_num < b_num ? -1 : a_rem > b_rem;  
});

我非常喜欢 georg 的解决方案,但我需要下划线(“_”)在数字之前进行排序。这是我修改他的代码的方法:

var chunkRgx = /(_+)|([0-9]+)|([^0-9_]+)/g;
function naturalCompare(a, b) {
    var ax = [], bx = [];
    
    a.replace(chunkRgx, function(_, $1, $2, $3) {
        ax.push([$1 || "0", $2 || Infinity, $3 || ""])
    });
    b.replace(chunkRgx, function(_, $1, $2, $3) {
        bx.push([$1 || "0", $2 || Infinity, $3 || ""])
    });
    
    while(ax.length && bx.length) {
        var an = ax.shift();
        var bn = bx.shift();
        var nn = an[0].localeCompare(bn[0]) || 
                 (an[1] - bn[1]) || 
                 an[2].localeCompare(bn[2]);
        if(nn) return nn;
    }
    
    return ax.length - bx.length;
}

/////////////////////////

test = [
    "img12.png",
    "img10.png",
    "img2.png",
    "img1.png",
    "img101.png",
    "img101a.png",
    "abc10.jpg",
    "abc10",
    "abc2.jpg",
    "20.jpg",
    "20",
    "abc",
    "abc2",
    "_abc",
    "_ab_c",
    "_ab__c",
    "_abc_d",
    "ab_",
    "abc_",
    "_ab_cd",
    ""
];

test.sort(naturalCompare)
document.write("<pre>" + JSON.stringify(test,0,3));

用前导零填充字符串中的数字,然后正常排序。

var naturalSort = function (a, b) {
    a = ('' + a).replace(/(\d+)/g, function (n) { return ('0000' + n).slice(-5) });
    b = ('' + b).replace(/(\d+)/g, function (n) { return ('0000' + n).slice(-5) });
    return a.localeCompare(b);
}

var naturalSortModern = function (a, b) {
    return ('' + a).localeCompare(('' + b), 'en', { numeric: true });
}

console.dir((["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"].sort(naturalSort)));

console.dir((["IL0 Foo", "PI0 Bar", "IL10 Baz", "IL3 Bob says hello"].sort(naturalSortModern)));