【问题标题】:How do I sort an array of objects based on the ordering of another array?如何根据另一个数组的顺序对对象数组进行排序?
【发布时间】:2012-04-03 02:17:22
【问题描述】:

我有一个对象列表:

[ { id: 4, name:'alex' }, { id: 3, name:'jess' }, { id: 9, name:'...' }, { id: 1, name:'abc' } ]

我还有另一个“顺序”正确的列表。

[ 3, 1, 9, 4]

如何根据键“id”将第一个列表与第二个列表的顺序相匹配? 结果应该是:

[ { id: 3, name:'jess' }, { id: 1, name:'abc' }, { id: 9, name:'...' }, { id: 4, name:'alex' } ]

【问题讨论】:

    标签: javascript arrays json algorithm sorting


    【解决方案1】:

    我认为最好的方法是将第一个列表的所有元素放入一个散列中,使用 id 值作为属性名称;然后通过遍历 id 列表、查找散列中的每个对象并将其附加到列表中来构建第二个列表。

    【讨论】:

      【解决方案2】:

      将列表变成一个对象,这样您将拥有order = { 3:0, 1:1, 9:2, 4:3},而不是order = [3, 1, 9, 4],然后执行以下操作

      function ( order, objects ){
           ordered_objects = []
           for( var i in objects ){
                 object = objects[i]
                 ordered_objects[ order[ object.id ] ] = object
           }
           return ordered_objects
      }
      

      【讨论】:

      • 我认为您缺少s...object = object[i]
      【解决方案3】:

      有点像这样:

      var data = [ { id: 4, name:'alex' }, { id: 3, name:'jess' }, { id: 9, name:'...' }, { id: 1, name:'abc' } ],
          order = [ 3, 1, 9, 4],    
          sorted = [],    
          items = {},
          i;
      
      for (i = 0; i < data.length; i++)
         items[data[i].id] = data[i];
      
      for (i = 0; i < order.length; i++)
         sorted.push(items[order[i]]);
      

      这个想法是将来自data 的项目放入一个对象中,使用id 作为属性名称——这样您就可以检索具有给定id 的项目,而无需搜索数组。 (否则,您必须使用嵌套循环,或在单个循环中使用 Array.indexOf() 函数,就性能而言,这实际上是一个嵌套循环。)

      这假定data 中没有两个元素具有相同的 id 属性。

      【讨论】:

        【解决方案4】:

        DEMO

        function sort(array, order) {
        
            //create a new array for storage
            var newArray = [];
        
            //loop through order to find a matching id
            for (var i = 0; i < order.length; i++) { 
        
                //label the inner loop so we can break to it when match found
                dance:
                for (var j = 0; j < array.length; j++) {
        
                    //if we find a match, add it to the storage
                    //remove the old item so we don't have to loop long nextime
                    //and break since we don't need to find anything after a match
                    if (array[j].id === order[i]) {
                        newArray.push(array[j]);
                        array.splice(j,1);
                        break dance;
                    }
                }
            }
            return newArray;
        }
        
        var newOrder = sort(oldArray,[3, 1, 9, 4]);
        console.log(newOrder);​
        

        【讨论】:

        • 假设没有重复的 id,如果你在 if 块内放一个 break 可以加快速度。
        【解决方案5】:

        嗯,简单的答案是,“对于这么小的一组数据,任何比无限循环成本更低的东西基本上都不会引起注意。”但是让我们试着回答这个“正确”。

        第二个数组中的顺序没有押韵或理由,它只是第一个数组主键上的外键列表(使用 SQL 术语)。因此,将它们视为键,并且我们希望有效地查找这些键,哈希表(对象)可能会以O(n) 方式(2*n,真的)最快“排序”,假设第一个数组称为objArray,第二个数组称为keyArray

        // Create a temporary hash table to store the objects
        var tempObj = {};
        // Key each object by their respective id values
        for(var i = 0; i < objArray.length; i++) {
            tempObj[objArray[i].id] = objArray[i];
        }
        // Rebuild the objArray based on the order listed in the keyArray
        for(var i = 0; i < keyArray.length; i++) {
            objArray[i] = tempObj[keyArray[i]];
        }
        // Remove the temporary object (can't ``delete``)
        tempObj = undefined;
        

        应该这样做。我想不出任何不需要两次通过的方法。 (或者一个接一个,像这样,或者通过数组多次传递并splice找出找到的元素,例如,这对于反向排序的数据可能会变得很昂贵。)

        【讨论】:

        • 您可以通过将 objArray.length 移动到 var 来进一步加快速度,这样就不会每次都对其进行评估。 (但你已经知道了:)这得到了我的 +1
        【解决方案6】:

        您可以使用 Alasql 库和两个数组的简单 SELECT JOIN 来做到这一点。

        唯一的一点:Alasql 将源数据理解为数组数组或对象数组,所以你 需要将简单数组转换为数组数组(参见步骤 1)

        var data1 = [ { id: 3, name:'jess' }, { id: 1, name:'abc' }, 
           { id: 9, name:'...' }, { id: 4, name:'alex' } ];
        var data2 = [3, 1, 9, 4];
        
        // Step 1: Convert [3,1,9,4] to [[3],[1],[9],[4]]
        var data2a = data2.map(function(d){return [d]});
        
        // Step 2: Get the answer
        var res = alasql('SELECT data1.* FROM ? data1 JOIN ? data2 ON data1.id = data2.[0]',
            [data1,data2a]);
        

        试试这个例子at jsFiddle

        【讨论】:

          【解决方案7】:

          我介入了这个问题并用一个简单的.sort解决了它

          假设您要排序的列表存储在变量needSort 中,并且具有顺序的列表在变量order 中,并且两者都在同一范围内,您可以像这样运行.sort

          needSort.sort(function(a,b){
            return order.indexOf(a.id) - order.indexOf(b.id);
          });
          

          它对我有用,希望它有所帮助。

          【讨论】:

          • 你的3行可以压缩:return order.indexOf(a.id) &lt; order.indexOf(b.id) ? -1 : 1;JSFiddle:jsfiddle.net/zamnuts/guyjm0za
          • 感谢您的建议,我已将其添加到答案中。
          • 嘿,如果两个数组的长度,即要排序的实际数组和顺序数组不同,这会起作用吗?而且,在这种情况下,如果我们需要进一步按 id 对其进行排序,如果 2 个 id 相同,我们该怎么做。
          • 不将此标记为答案,您就是在犯罪。
          • 我认为这个答案有问题,因为如果两个索引相等,排序函数不会返回0。这是病态的,因为只有当需要排序中的两个元素具有相同的id 时才会发生这种情况。因此我会使用return order.indexOf(a.id) - order.indexOf(b.id);
          【解决方案8】:

          我如何解决几乎相同的问题

          data = [{ id: 4, name:'alex' }, { id: 3, name:'jess' }, { id: 9, name:'...' }, { id: 1, name:'abc' } ];
          
          sorted = [3, 1, 9, 4].map((i) => data.find((o) => o.id === i));
          

          【讨论】:

          • 此代码假定[3, 1, 9, 4] 中的每个元素在data 中都有一个元素。如果数据只包含一个子集,那么排序列表可能包含undefined 元素。
          • 是的。不过,可以通过附加 .filter(o =&gt; o) 之类的内容轻松修复,具体取决于您要查找的内容。
          【解决方案9】:
          const obj = [ { id: 4, name:'alex' }, { id: 3, name:'jess' }, { id: 9, name:'...' }, { id: 1, name:'abc' } ];
          const id = [ 3, 1, 9, 4];
          const result = id.map(i => obj.find(j => j.id === i));
          

          【讨论】:

            猜你喜欢
            • 2022-01-28
            • 1970-01-01
            • 1970-01-01
            • 2013-11-10
            • 1970-01-01
            • 2015-06-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多