【问题标题】:Sort Array of numeric & alphabetical elements (Natural Sort)排序数字和字母元素的数组(自然排序)
【发布时间】:2010-12-07 03:02:07
【问题描述】:

假设我有一个数组

var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];

我尝试对其进行排序,我得到类似...

[1, 1, 10, 2, 2, 3, 5, 55, 7, 75, 8, "abc", "ahsldk", "huds"]

通知 10 在 2 之前,我怎么能有更多类似的东西

[1,1,2,2,3,5 ..., "abc", "ahs...",...]

【问题讨论】:

标签: javascript arrays sorting


【解决方案1】:

根据原始问题,简短而甜蜜:

var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];
arr.sort(function(a,b){
  var a1=typeof a, b1=typeof b;
  return a1<b1 ? -1 : a1>b1 ? 1 : a<b ? -1 : a>b ? 1 : 0;
});
// [1, 1, 2, 2, 3, 5, 7, 8, 10, 55, 75, "abc", "ahsldk", "huds"]

(首先按类型排序,然后按值排序。)


功能更全面的自然排序:

var items = ['a1c', 'a01', 'a1', 'a13', 'a1a', 'a1b', 'a3b1', 'a1b0',
             'a1b3', 'a1b1', 'dogs', 'cats', 'hogs', 'a2', '2', '20',
             1, 13, 1.1, 1.13, '1.2', 'a'];
 
console.log(naturalSort(items))
 
function naturalSort(ary, fullNumbers) {
  var re = fullNumbers ? /[\d\.\-]+|\D+/g : /\d+|\D+/g;

  // Perform a Schwartzian transform, breaking each entry into pieces first
  for (var i=ary.length;i--;)
    ary[i] = [ary[i]].concat((ary[i]+"").match(re).map(function(s){
      return isNaN(s) ? [s,false,s] : [s*1,true,s];
    }));

  // Perform a cascading sort down the pieces
  ary.sort(function(a,b){
    var al = a.length, bl=b.length, e=al>bl?al:bl;
    for (var i=1;i<e;++i) {
      // Sort "a" before "a1"
      if (i>=al) return -1; else if (i>=bl) return 1;
      else if (a[i][0]!==b[i][0])
        return (a[i][1]&&b[i][1]) ?        // Are we comparing numbers?
               (a[i][0]-b[i][0]) :         // Then diff them.
               (a[i][2]<b[i][2]) ? -1 : 1; // Otherwise, lexicographic sort
    }
    return 0;
  });

  // Restore the original values into the array
  for (var i=ary.length;i--;) ary[i] = ary[i][0];
  return ary;
}

使用naturalSort,如果您希望“1.13”排在“1.2”之前,请将true 作为第二个参数传递。

【讨论】:

  • 如果我的字符串中有一个数字"55" 排序错误,jsfiddle.net/8VjWL,它通常可以工作
  • @jiewmeng 这不是你问题的一部分。此外,如果字符串中有一个数字......那么你有一个字符串而不是一个数字,你应该更精确地填充你的数组。 :p (如果你真的想要,你可以添加 *1parseFloat 作为你的第一个排序标准,但我鼓励你只有在你真的必须接受带有数字作为字符串的数组时才这样做。)
  • +1 绝对更简单的代码,用于没有数字伪装成字符串的更简洁的情况。
  • 虽然我的回答很简短,但明确键入所有这些后备案例很烦人。我写了Array.sortBy 是为了方便这种事情。你可以用它来解决这个问题:arr.sortBy( function(o){ return [typeof o, o] } );
  • 又短又甜:)
【解决方案2】:

来自 mrhoo 的 http://snipplr.com/view/36012/javascript-natural-sort/

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) return a1> b1? 1: -1;
        }
        return a.length- b.length;
    });
}

或者,来自 Alphanum:Javascript Natural Sorting Algorithm by Brian Huisman

Array.prototype.alphanumSort = function(caseInsensitive) {
  for (var z = 0, t; t = this[z]; z++) {
    this[z] = [];
    var x = 0, y = -1, n = 0, i, j;

    while (i = (j = t.charAt(x++)).charCodeAt(0)) {
      var m = (i == 46 || (i >=48 && i <= 57));
      if (m !== n) {
        this[z][++y] = "";
        n = m;
      }
      this[z][y] += j;
    }
  }

  this.sort(function(a, b) {
    for (var x = 0, aa, bb; (aa = a[x]) && (bb = b[x]); x++) {
      if (caseInsensitive) {
        aa = aa.toLowerCase();
        bb = bb.toLowerCase();
      }
      if (aa !== bb) {
        var c = Number(aa), d = Number(bb);
        if (c == aa && d == bb) {
          return c - d;
        } else return (aa > bb) ? 1 : -1;
      }
    }
    return a.length - b.length;
  });

  for (var z = 0; z < this.length; z++)
    this[z] = this[z].join("");
}

【讨论】:

  • 两者有什么区别吗?我对它们进行测试的第一印象是,也许第二个(Opera)1 会更可靠,因为它来自 Opera,但是在测试后,jsfiddle.net/sqcFD,我发现我在那个 1 中出错了。也许更短的 1对我有用。
  • @jiewmeng 我只测试了第一个 - 我假设(基于分析的深度和快速浏览代码)第二个是可靠的......如果是的话,坚持第一个为你工作。
  • 注意Array.prototype.alphanumSort 要求输入数组只包含字符串。
  • @JiewMeng "也许 2nd (Opera) 1 会更可靠,因为它来自 Opera" : 它不是来自 Opera,它只是 GreyWyvern (Brian Huisman) 在 Opera 博客托管上的博客服务。见web.archive.org/web/20130826203933/http://my.opera.com/…
  • 另请注意,Brian Huisman 的灵感来自 Dave Koelle 在 web.archive.org/web/20131005224909/http://www.davekoelle.com/… 上的 Alphanum Algorithm@
【解决方案3】:

您可以使用String.prototype.localCompare() 在一行中完成此操作并获得您正在寻找的结果。请注意,numeric collat​​ion 选项已启用。

var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];

arr.sort((a,b) => ("" + a).localeCompare(b, undefined, {numeric: true}));

console.log(arr);
// [1, 1, 2, 2, 3, 5, 7, 8, 10, 55, 75, "abc", "ahsldk", "huds"]

也许添加一些逻辑来处理空值。

请注意,这仅适用于整数。浮点数不会按照您希望的方式排序。

【讨论】:

    【解决方案4】:

    // 大多数自然排序用于对字符串进行排序, 所以file2 排在file10 之前。

    如果您要混合实际数字,则需要将它们排序到数组的前面, 因为用连字符分隔的负数和数字很难解释。 带前导零的字符串需要小心,所以part002 将排在part010 之前。

    var natSort=function(as, bs) {
        var a, b, a1, b1,
        rx=  /(\d+)|(\D+)/g, rd= /\d/, rz=/^0/;
        if(typeof as=='number' || typeof bs=='number'){
            if(isNaN(as))return 1;
            if(isNaN(bs))return -1;
            return as-bs;
        }
        a= String(as).toLowerCase();
        b= String(bs).toLowerCase();
        if(a=== b) return 0;
        if(!(rd.test(a) && rd.test(b))) return a> b? 1: -1;
        a= a.match(rx);
        b= b.match(rx);
        while(a.length && b.length){
            a1= a.shift();
            b1= b.shift();
            if(a1!== b1){
                if(rd.test(a1) && rd.test(b1)){
                    return a1.replace(rz,'.0')- b1.replace(rz,'.0');
                }
                else return a1> b1? 1: -1;
            }
        }
        return a.length - b.length;
    }
    
    array.sort(natSort)
    

    【讨论】:

    • 看起来和jball的第一个例子很像,因为我又名mrhoo。
    • 这应该可行,但由于使用了正则表达式,因此对于大型数组可能会很慢:/
    【解决方案5】:

    这是一个精致的。

    var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"56","abc","huds"];
        arr.sort(
                    function (a,b){
                        if ( isNaN(a)&&isNaN(b)) return a<b?-1:a==b?0:1;//both are string
                        else if (isNaN(a)) return 1;//only a is a string
                        else if (isNaN(b)) return -1;//only b is a string
                        else return a-b;//both are num
                    }
        );
    

    结果:1​​|1|2|2|3|5|7|8|10|55|56|75|abc|ahsldk|huds|

    【讨论】:

    • chrome 中的快速配置文件会话显示此答案是最快的。它比 Phrogz 解决方案略快,比 jball 的任何一种解决方案都快一个数量级。
    • 我使用 temp vars var as = isNaN(a), bs = isNaN(b); 测量了 10% 的速度提升
    • 很容易看到正则表达式吞噬了处理。也可能是 typeof?
    • 很好 - 希望这不会被用于对性能至关重要的情况,但代码的可读性提高,因为这是个好消息。
    • @pinichi - 测试时我注意到你在字符串比较函数中使用了a=b;我假设您的意思是 a==b(这对我来说似乎工作正常)并且没有使用一些神秘的 js 分配作为比较技巧?
    【解决方案6】:

    如果你只有字母和整数项目,你可以坚持使用简单的代码:

    var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];
    arr.sort(function(a, b)
    {
        if (a == b)
            return 0;
    
        var n1 = parseInt(a, 10);
        var n2 = parseInt(b, 10);
        if (isNaN(n1) && isNaN(n2)) {
            //both alphabetical
            return (a > b) ? 1 : 0;
        }
        else if (!isNaN(n1) && !isNaN(n2)) {
            //both integers
            return (n1 > n2) ? 1 : 0;
        }
        else if (isNaN(n1) && !isNaN(n2)) {
            //a alphabetical and b is integer
            return 1;
        }
    
        //a integer and b is alphabetical
        return 0;
    });
    

    工作示例:http://jsfiddle.net/25X2e/

    【讨论】:

    • 我不确定我是否会将其归类为“简单”:)
    【解决方案7】:

    如果您总是可以假设数字和字符串未混合的 alpha,我只会分而治之。使用 typeof 将数字分割成一个新数组。独立排序,然后加入两个数组。

    【讨论】:

      【解决方案8】:

      我也知道以下可以按字母数字顺序对数组进行排序的方法。

      const arr = [1, 5, "ahsldk", 10, 55, 3, 2, 7, 8, 1, 2, 75, "abc", "huds"];
      arr.sort((a, b) => a - b || a.toString().localeCompare(b.toString()));
      console.log(arr)

      【讨论】:

        猜你喜欢
        • 2013-03-06
        • 2017-06-08
        • 1970-01-01
        • 2013-10-15
        • 1970-01-01
        • 2010-11-23
        • 2011-02-17
        相关资源
        最近更新 更多