如何根据另一个数组的顺序对对象数组进行排序?

Posted

技术标签:

【中文标题】如何根据另一个数组的顺序对对象数组进行排序?【英文标题】: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'  ]

【问题讨论】:

【参考方案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));

【讨论】:

以上是关于如何根据另一个数组的顺序对对象数组进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

我们如何根据节点js中的另一个对象数组值对对象数组进行排序

js对象数组按照另一个数组排序

根据另一个 id 数组对对象数组进行排序

根据字符串属性按字母顺序对对象数组进行排序

javascript 根据另一个数组顺序排序对象数组

根据另一个数组排序顺序对多个数组进行排序