按字母顺序、数字顺序、特殊字符顺序对字符串数组进行排序

4

给定以下字符串数组,我一直在尝试将a-z字符串自然排序,其次是数字字符串,最后是特殊字符。

    nextSearchTerms = ["T","D","I","C","Y","O","4","K","N","800","S","1","V","(","10","'","`","B","M","[","-"," ","J","U","H","G","R","E","P"];
    console.log(nextSearchTerms);

    Array.prototype.naturalSort = function(){
        var a, b, a1, b1, rx=/(\d+)|(\D+)/g, rd=/\d+/;
        return this.sort(function(as, bs){
            a= String(as).toLowerCase().match(rx);
            b= String(bs).toLowerCase().match(rx);
            while(a.length && b.length){
                a1= a.shift();
                b1= b.shift();
                if (rd.test(a1) || rd.test(b1)){
                    if(!rd.test(a1)) return -1;
                    if(!rd.test(b1)) return 1;
                    if(a1 != b1) return a1-b1;
                }
                else if (a1 != b1) {
                    var aIsLetter = a1[0].charAt(0).match(/[a-z]/i),
                        bIsLetter = b1[0].charAt(0).match(/[a-z]/i);
                    if (aIsLetter && !bIsLetter) return -1;
                    if (!aIsLetter && bIsLetter) return 1;
                    return (a1[0] == b1[0] ? 0 : (a1[0] < b1[0] ? -1 : 1));
                }
            }
            return a.length - b.length;
        });
    }
    console.log(nextSearchTerms.naturalSort());

我一直在尝试修改的函数目前返回以下内容:
["B", "C", "D", "E", "G", "H", "I", "J", "K", "M", "N", "O", "P", "R", "S", "T", "U", "V", "Y", " ", "'", "(", "-", "[", "`", "1", "4", "10", "800"]
我希望最终的数组输出为:
["B", "C", "D", "E", "G", "H", "I", "J", "K", "M", "N", "O", "P", "R", "S", "T", "U", "V", "Y", "1", "4", "10", "800", "'", "(", "-", "[", "`"," "]
请问我漏掉了什么?

数组会改变吗?在相同类型的字符(例如符号)之间,您希望它们以什么顺序排列? - tudor.gergely
请查看natural sort - Bergi
@tudor.gergely 当然,它们肯定会经常变化。关于特殊字符的顺序,我只有一个偏好,就是它们排在最后(在a-z和0-9之后)。 - Seigs
@Seigs 我明白了。我修改了我的解决方案,现在也适用于数字。看一下吧。 - tudor.gergely
4个回答

2
这是我对你所寻求的东西的尝试。我认为它比你现有的更简洁:
Array.prototype.naturalSort = function() {
  var stringRE = /^[A-Za-z]+$/
  var numberRE = /^[\d]+$/
  return this.sort(function(a, b) {
    var aIsString = stringRE.test(a);
    var bIsString = stringRE.test(b)
    var aIsNumeric = numberRE.test(a);
    var bIsNumeric = numberRE.test(b);
    if (aIsString && bIsString) {
      return a.localeCompare(b);
    } else if (aIsNumeric && bIsNumeric) {
      return parseInt(a, 10) - parseInt(b, 10);
    } else if (aIsString && bIsNumeric) {
      return -1;
    } else if (aIsNumeric && bIsString) {
      return 1;
    } else if (aIsString || aIsNumeric) {
      return -1;
    } else if (bIsString || bIsNumeric) {
      return 1;
    } else {
      return a.localeCompare(b);
    }
  })
};

var chars = ["T","D","I","C","Y","O","4","K","N","800","S","1","V","(","10","'","`","B","M","[","-"," ","J","U","H","G","R","E","P"];

console.log(chars.naturalSort());
// ["B", "C", "D", "E", "G", "H", "I", "J", "K", "M", "N", "O", "P", "R", "S", "T", "U", "V", "Y", "4", "1", "10", "800", " ", "-", "'", "(", "[", "`"]

更加简洁了,我不得不改变aIsNumeric && bIsNumeric测试的返回值为return parseInt(a, 10)-parseInt(b, 10),以使数字字符串自然排序。现在返回值为["B", "C", "D", "E", "G", "H", "I", "J", "K", "M", "N", "O", "P", "R", "S", "T", "U", "V", "Y", "1", "4", "10", "800", " ", "-", "'", "(", "[", "\"]`。 - Seigs

0

这样怎么样?当某物与数字进行比较时...

            if (rd.test(a1) || rd.test(b1)){
                var aIsLetter = a1[0].charAt(0).match(/[a-z]/i),
                    bIsLetter = b1[0].charAt(0).match(/[a-z]/i);
                if(!rd.test(a1) && aIsLetter) return -1;
                if(!rd.test(a1) && !aIsLetter) return 1;
                if(!rd.test(b1) && bIsLetter) return 1;
                if(!rd.test(b1) && !bIsLetter) return -1;
                if(a1 != b1) return a1-b1;
            }

0

以下是一种更简短的解决方案:

function naturalSort(a, b) {
  var aPriority = /[a-z]/i.test(a) * 3 + /\d+/i.test(a) * 2; 
  var bPriority = /[a-z]/i.test(b) * 3 + /\d+/i.test(b) * 2; 

  if (aPriority === bPriority) return a.localeCompare(b, 'en', {numeric: true});
  return aPriority < bPriority ? 1: -1;
}

nextSearchTerms.sort(naturalSort)
// ["B", "C", "D", "E", "G", "H", "I", "J", "K", "M", "N", "O", "P", "R", "S", "T", "U", "V", "Y", "1", "4", "10", "800", " ", "-", "'", "(", "[", "`"]

如果我理解正确的话,这应该能回答你的问题。它相当简短易懂。字母具有更高的优先级(3),数字为2,其余为1。而且数组按优先级顺序排序。
附:已修复数字排序问题;

好的方法,但无法按需要对小数字符串进行排序。 - Redu
这将使“3”在“29”之后排序。 - user663031
@Seigs,我改变了解决方案,使其仍然保持小巧,但也适用于数字。也许现在你会喜欢它了。 - tudor.gergely

-1

我想以下的方法可以完成所需的任务。实际上,该函数只是利用可链接的数组函数的单一行代码。为了更好的理解,我将它分成了三行。按照以下步骤操作:

  1. 将数组归约为3个不同类型的数组。在此过程中,将小数转换为数字,因为下一步需要这样做。
  2. 通过对类型数组进行排序来减少数组,并将它们映射回字符串(如果它们是数字类型)并与先前的字符串连接。

我相信它可以进一步简化,但到目前为止,这就是我能想到的。请给予建议...

var testar = ["T","D","I","C","Y","O","4","K","N","800","S","1","V","(","10","'","`","B","M","[","-"," ","J","U","H","G","R","E","P", "0", "-17"];
function naturalSort(a){
  return a.reduce((p,c) => {
                             /[A-Z]/i.test(c) ? p[0].push(c)   :
                             /\d+/.test(c)    ? p[1].push(c*1) : p[2].push(c);
                             return p
                           }, [[],[],[]])
          .reduce((p,c) => p.concat(c.sort((p,c) => p < c ? -1:1)
          .map(e => typeof e == "number" ? e.toString() : e)),[]);
}
document.write('<pre>' + JSON.stringify(naturalSort(testar), 0, 2) + '</pre>');


投票者请说明在您按下负号的那一刻您究竟在想什么。这实际上是“唯一一个”真正回答问题的答案。即使被接受的答案在插入负数(如“-17”)时也会失败。如果您有任何想法可以使此代码更有效,请提出并与我分享。如果此段代码中有任何您不想看到的内容,请让我知道。您为什么要投反对票,我真的很好奇。我怎么让您不满意了呢..? - Redu

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