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

15
我有一个对象列表:
[ { 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' } ]
9个回答

30

我遇到了这个问题,用一个简单的 .sort解决了它。

假设需要排序的列表存储在变量needSort中,排序顺序的列表存储在变量order中,并且两者在相同的作用域中,你可以像这样运行.sort

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

对我有用,希望能帮到你。


2
你的三行代码可以压缩成:return order.indexOf(a.id) < order.indexOf(b.id) ? -1 : 1; JSFiddle链接:http://jsfiddle.net/zamnuts/guyjm0za/ - zamnuts
谢谢建议,我已将其添加到答案中。 - HClx
嘿,如果要排序的实际数组和顺序数组的长度不同,这样会起作用吗?而且,在这种情况下,如果我们需要按id进一步排序,如果有2个id相同,我们该怎么做。 - whyAto8
2
你没有将这个标记为答案是一种犯罪行为。 - VHanded
2
我认为这个答案有问题,因为排序函数如果两个索引相等时不会返回0。这是病态的,因为只有当需要排序的两个元素具有相同的id时才会发生这种情况。因此,我会使用return order.indexOf(a.id) - order.indexOf(b.id); - Michael_Scharf
在我的情况下很好,必须将两个都修改为字符串,这样就可以完美地工作了:return order.indexOf('' + a.id) - order.indexOf('' + b.id); - Ylama

10

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

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 中的一个元素。如果 data 只包含其中一部分,则排序后的列表中可能包含 undefined 元素。 - Michael_Scharf
True。根据您的需求,可以通过附加类似于.filter(o => o)的内容轻松修复。 - Frans

7

简单的回答是,对于这么少的数据集,任何比无限循环更低成本的方法都基本上不会被注意到。但让我们试着“正确”回答。

第二个数组的顺序没有任何规律,它只是第一个数组主键的外键列表(使用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移动到一个变量中,以进一步提高速度,这样它就不会每次被评估了。(但你已经知道了:) 我给你点赞。 - Aran Mulholland

1
将列表转换为对象,所以不再是order = [3, 1, 9, 4],而是order = { 3:0, 1:1, 9:2, 4:3},然后执行以下操作。
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]. - James Montagne

1

大概是这样的:

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属性。


1

演示

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,你可以加快运行速度。 - nnnnnn

1

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


0
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));

0

你可以使用Alasql库中简单的SELECT JOIN操作来完成它。

唯一需要注意的是:Alasql将源数据理解为数组或对象数组,因此你需要将简单的数组转换为数组嵌套的形式(请参见第一步)。

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]);

试试这个例子在 jsFiddle 上


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