【问题标题】:Javascript natural sort array/object and maintain index associationJavascript自然排序数组/对象并维护索引关联
【发布时间】:2011-04-18 23:20:07
【问题描述】:

我在 Javascript 中有一个如下的项目数组:

var users = Array();

users[562] = 'testuser3';
users[16] = 'testuser6';
users[834] = 'testuser1';
users[823] = 'testuser4';
users[23] = 'testuser2';
users[917] = 'testuser5';

我需要对该数组进行排序以获得以下输出:

users[834] = 'testuser1';
users[23] = 'testuser2';
users[562] = 'testuser3';
users[823] = 'testuser4';
users[917] = 'testuser5';
users[16] = 'testuser6';

注意它是如何按数组的值排序的,并且在数组排序后保持值到索引的关联(这很关键)。我一直在寻找解决方案,尝试制作它,但碰壁了。

顺便说一句,我知道这在技术上不是一个数组,因为这意味着索引总是从 0 到 n 迭代,其中 n+1 是进行 n 的计数。无论您如何定义它,对项目的要求仍然是相同的。另外,如果它有所作为,我不会使用 jquery。

【问题讨论】:

    标签: javascript associative-array sorting


    【解决方案1】:

    数组元素的顺序由索引定义。因此,即使您以不同的顺序指定值,这些值也将始终按其索引的顺序存储,未定义的索引为undefined

    > var arr = [];
    > arr[2] = 2;
    > arr[0] = 0;
    > arr
    [0, undefined, 2]
    

    现在如果你想存储索引和值对,你需要一个不同的数据结构,可能是这样的数组数组:

    var arr = [
        [562, 'testuser3'],
        [16, 'testuser6'],
        [834, 'testuser1'],
        [823, 'testuser4'],
        [23, 'testuser2'],
        [917, 'testuser5']
    ];
    

    这可以用这个比较函数排序:

    function cmp(a, b) {
        return a[1].localeCompare(b[1]);
    }
    arr.sort(cmp);
    

    结果是这个数组:

    [
        [834, 'testuser1'],
        [23, 'testuser2'],
        [562, 'testuser3'],
        [823, 'testuser4'],
        [917, 'testuser5'],
        [16, 'testuser6']
    ]
    

    【讨论】:

      【解决方案2】:

      如果我正确理解了这个问题,您使用数组的方式并不适合它们。其实就是初始化风格

      // Don't do this!
      var array = new Array();
      array[0] = 'value';
      array[1] = 'value';
      array[2] = 'value';
      

      对数组的性质和用途提出了错误的看法。数组是一个有序的项目列表,从零开始索引。创建数组的正确方法是使用数组字面量

      var array = [
          'value',
          'value',
          'value'
      ]
      

      索引是根据指定项目的顺序隐含的。创建一个数组并设置users[562] = 'testuser3' 意味着列表中至少有 562 个其他用户,并且您有理由此时只知道第 563 个。

      在您的情况下,索引是数据,并不代表集合中项目的顺序。您正在寻找的是地图或字典,在 JavaScript 中由 普通对象 表示:

      var users = {
          562: 'testuser3',
          16:  'testuser6',
          834: 'testuser1',
          823: 'testuser4',
          23:  'testuser2',
          917: 'testuser5'
      }
      

      现在您的集合没有订单,但确实有有意义的键。从这里,您可以按照galambalazs's advice 创建对象键的数组:

      var userOrder;
      if (typeof Object.keys === 'function') {
          userOrder = Object.keys(users);
      } else {
          for (var key in users) {
              userOrder.push(key);
          }
      }
      

      …然后排序:

      userOrder.sort(function(a, b){
          return users[a].localeCompare(users[b]);
      });
      

      Here's a demo

      【讨论】:

      • @Gumbo 我同意!我的答案与此有何不同?
      • 没关系。不知何故,我以为您正在尝试对对象的属性进行排序。
      【解决方案3】:

      您不能在 Javascript 中对这样的数组进行排序。最好的办法是制作订单地图

      order = new Array();
      order[0] = 562;
      order[1] = 16;
      order[2] = 834;
      order[3] = 823;
      order[4] = 23;
      order[5] = 917;
      

      通过这种方式,您可以拥有任何您想要的顺序,而与原始数组中的键无关。 要对数组进行排序,请使用自定义排序函数

      order.sort( function(a, b) {
        if ( users[a] < users[b] ) return -1;
        else if ( users[a] > users[b] ) return 1;
        else return 0;
      });
      
      for ( var i = 0; i < order.length; i++ ) {
        // users[ order[i] ]
      }
      

      [Demo]

      【讨论】:

        【解决方案4】:

        利用 cmets 的想法,我想出了以下解决方案。 naturalSort 函数是我在 google 上找到的,我对其进行了修改以对多维数组进行排序。基本上,我使 users 数组成为一个多维数组,第一个索引是用户 ID,第二个索引是用户名。所以:

        users[0][0] = 72;
        users[0][1] = 'testuser4';
        users[1][0] = 91;
        users[1][1] = 'testuser2';
        users[2][0] = 12;
        users[2][1] = 'testuser8';
        users[3][0] = 3;
        users[3][1] = 'testuser1';
        users[4][0] = 18;
        users[4][1] = 'testuser7';
        users[5][0] = 47;
        users[5][1] = 'testuser3';
        users[6][0] = 16;
        users[6][1] = 'testuser6';
        users[7][0] = 20;
        users[7][1] = 'testuser5';
        

        然后我对数组进行排序以获得以下输出:

        users_sorted[0][0] = 3;
        users_sorted[0][1] = 'testuser1';
        users_sorted[1][0] = 91;
        users_sorted[1][1] = 'testuser2';
        users_sorted[2][0] = 47;
        users_sorted[2][1] = 'testuser3';
        users_sorted[3][0] = 72;
        users_sorted[3][1] = 'testuser4';
        users_sorted[4][0] = 20;
        users_sorted[4][1] = 'testuser5';
        users_sorted[5][0] = 16;
        users_sorted[5][1] = 'testuser6';
        users_sorted[6][0] = 18;
        users_sorted[6][1] = 'testuser7';
        users_sorted[7][0] = 12;
        users_sorted[7][1] = 'testuser8';
        

        执行此操作的代码如下:

        function naturalSort(a, b) // Function to natural-case insensitive sort multidimensional arrays by second index
        {
        
            // setup temp-scope variables for comparison evauluation
            var re = /(-?[0-9\.]+)/g,
                x = a[1].toString().toLowerCase() || '',
                y = b[1].toString().toLowerCase() || '',
                nC = String.fromCharCode(0),
                xN = x.replace( re, nC + '$1' + nC ).split(nC),
                yN = y.replace( re, nC + '$1' + nC ).split(nC),
                xD = (new Date(x)).getTime(),
                yD = xD ? (new Date(y)).getTime() : null;
            // natural sorting of dates
            if ( yD )
                if ( xD < yD ) return -1;
                else if ( xD > yD ) return 1;
            // natural sorting through split numeric strings and default strings
            for( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
                oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
                oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
                if (oFxNcL < oFyNcL) return -1;
                else if (oFxNcL > oFyNcL) return 1;
            }
            return 0;
        }
        
        // Set values for index
            var users = Array();
            var temp = Array();
        
            users.push(Array('72', 'testuser4'));
            users.push(Array('91', 'testuser2'));
            users.push(Array('12', 'testuser8'));
            users.push(Array('3', 'testuser1'));
            users.push(Array('18', 'testuser7'));
            users.push(Array('47', 'testuser3'));
            users.push(Array('16', 'testuser6'));
            users.push(Array('20', 'testuser5'));
        
        // Sort the array
            var users_sorted = Array();
            users_sorted = users.sort(naturalSort);
        

        【讨论】:

        • 嘿,我很高兴你找到了一个可行的答案,但我鼓励你快速查看上面的my answer(我玩游戏有点晚了!)。字符串比较内置在 JavaScript 中,您可能会发现它总体上是一种更简洁的解决方案。
        • 您还应该查看数组和对象文字(我只在回答中简要提及它们)。您可以在示例代码中更快地创建 users 数组:var users = [['72', 'testuser4'], ['91', 'testuser2'], …]
        • 感谢您的提醒,它看起来确实更干净。它是否支持自然案例排序?这就是让我的代码有点草率的原因。
        • 它执行"locale-sensitive string comparison",它可以让你大部分时间到达那里。如果您需要它不区分大小写,请在调用 localeCompare 之前将字符串设为小写:['Foo', 'foo', 'aardvark', 'Baardvark'].sort(function(a, b){ return a.toLowerCase().localeCompare(b.toLowerCase()) }) -&gt; ["aardvark", "Baardvark", "Foo", "foo"]
        【解决方案5】:

        我会使用一次 map 来创建一个新的用户数组, 然后第二次从新数组中返回你想要的字符串。

        var users= [];
        users[562]= 'testuser3';
        users[16]= 'testuser6';
        users[834]= 'testuser1';
        users[823]= 'testuser4';
        users[23]= 'testuser2';
        users[917]= 'testuser5';
        
        var u2= [];
        users.map(function(itm, i){
            if(itm){
                var n= parseInt(itm.substring(8), 10);
                u2[n]= i;
            }
        });
        u2.map(function(itm, i){
            return 'users['+itm+']= testuser'+i;
        }).join('\n');
        
        /*returned value: (String)
        users[834]= testuser1
        users[23]= testuser2
        users[562]= testuser3
        users[823]= testuser4
        users[917]= testuser5
        users[16]= testuser6
        */
        

        如果你想避免任何差距。在输出上使用一个简单的过滤器-

        u2.map(function(itm, i){
            return 'users['+itm+']= testuser'+i;
        }).filter(function(itm){return itm}).join('\n');
        

        【讨论】:

          【解决方案6】:

          稀疏数组通常会带来麻烦。最好将数组中的键值对保存为对象(这种技术也是有效的 JSON):

          users = [{
              "562": "testuser3"
          },{
              "16": "testuser6"
          }, {
              "834": "testuser1"
          }, {
              "823": "testuser4"
          }, {
              "23": "testuser2"
          }, {
              "917": "testuser5"
          }];
          

          按照建议,您可以使用 for 循环将排序函数映射到数组。

          【讨论】:

            【解决方案7】:

            Array.prototype.sort() 采用可选的自定义比较函数——因此,如果您以这种方式将所有 users 转储到数组中,[ [562, "testuser3"], [16, "testuser6"] ... etc.]

            然后sort这个数组有如下函数:

            function(comparatorA, comparatorB) {
                var userA = comparatorA[1], userB = comparatorB[1]
                if (userA > userB)     return 1;
                if (userA < userB)     return -1;
                if (userA === userB)   return 0;
            }
            

            然后重建您的 users 对象。 (这会使您失去排序。)或者,将数据保留在新排序的数组数组中,如果这适用于您的应用程序。

            【讨论】:

              【解决方案8】:

              一个带有数组数组的单行器:

              用于按键排序。

              let usersMap = users.map((item, i) => [i, item]).sort((a, b) => a[0] - b[0]);
              

              用于按值排序。 (适用于原始类型)

              let usersMap = users.map((item, i) => [i, item]).sort((a, b) => a[1] - b[1]);
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-08-17
                • 1970-01-01
                • 1970-01-01
                • 2012-03-12
                • 1970-01-01
                • 1970-01-01
                • 2015-04-04
                • 1970-01-01
                相关资源
                最近更新 更多