Как отсортировать массив объектов на основе порядка другого массива? - PullRequest
12 голосов
/ 18 марта 2012

У меня есть список объектов:

[ { 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' } ]

Ответы [ 8 ]

26 голосов
/ 20 февраля 2015

Я вмешался в эту проблему и решил ее простым .sort

Предполагая, что ваш список для сортировки хранится в переменной needSort, а список с порядком - в переменной order, и оба находятся в одной и той же области видимости, вы можете запустить .sort следующим образом:

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

Это сработало для меня, надеюсь, это поможет.

7 голосов
/ 18 марта 2012

Ну, простой ответ будет: «для такого маленького набора данных все, что дешевле, чем бесконечный цикл, будет в основном незаметным».Но давайте попробуем ответить на это «правильно».

Нет порядка или причины для порядка во втором массиве, это просто список внешних ключей (для использования терминологии 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 извлечения найденных элементов, что может быть дорогостоящим, например, при сортировке в обратном порядке.)

6 голосов
/ 01 февраля 2017

Как я решил почти ту же проблему

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));
2 голосов
/ 18 марта 2012

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);​
1 голос
/ 18 марта 2012

Немного примерно так:

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 в объект, используя идентификаторы в качестве имен свойств - таким образом, вы можете получить элемент с заданным идентификатором без необходимости поиска в массиве. (В противном случае вам придется использовать вложенный цикл или функцию Array.indexOf() внутри одного цикла, который фактически будет вложенным циклом в плане производительности.)

Предполагается, что никакие два элемента в data не имеют одинакового свойства id.

1 голос
/ 18 марта 2012

Создайте список в объект, так что вместо 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
}
1 голос
/ 18 марта 2012

Я думаю, что лучший способ, который вы найдете, - это поместить все элементы первого списка в хеш, используя значения id в качестве имени свойства;затем создайте второй список, перебирая список идентификаторов, просматривая каждый объект в хэше и добавляя его в список.

0 голосов
/ 18 декабря 2014

Вы можете сделать это с помощью библиотеки 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]);

Попробуйте этот пример в jsFiddle .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...