Подтвердить что ты не робот

Используя метод "разности" подчёркивания на массивах объектов

_.difference([], [])

этот метод отлично работает, когда у меня есть данные примитивного типа, например

var a = [1,2,3,4];
var b = [2,5,6];

а вызов _.difference(a,b) возвращает [1,3,4]

но в случае, если я использую объект вроде

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

похоже, не работает

4b9b3361

Ответ 1

Причина просто в том, что объект с одним и тем же содержимым не является одним и тем же объектом, например.

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}]; 
a.indexOf({'id':1, 'value':10})

Он не вернет 0, а -1, потому что мы ищем другой объект

Смотрите исходный код http://underscorejs.org/underscore.js, _.difference использует _.contains

_.difference = function(array) {
  var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
  return _.filter(array, function(value){ return !_.contains(rest, value); });
};

и _.contains в конечном счете использует indexOf, следовательно, не найдет объекты, если они не указывают на один и тот же объект.

Вы можете улучшить подчеркивание _.contains путем циклического перехода по всем элементам и вызова обратного вызова сравнения, который вы должны иметь возможность передать в разницу или содержать функцию, или вы можете проверить эта версия, которая улучшает, содержит методы

Ответ 2

попробуйте это для размера для нахождения разности массива объектов:

var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];

_.filter(test, function(obj){ return !_.findWhere(test2, obj); });

Ответ 3

Хотя принятый ответ верен, а другие ответы дают хорошие идеи, есть еще один вариант, который довольно легко реализовать с помощью подчеркивания.

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

Используя метод подчеркивания "выщипывание", вы можете быстро построить массив всех идентификаторов в вашем исходном наборе и целевом наборе. Оттуда все методы подчеркивания массива будут работать, разница, объединение, пересечение и т.д.

После операции тривиально получить список объектов из вашего исходного списка, который вы хотите. Вот пример:

Многословный:

var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];

var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

или, более кратко:

var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });

Конечно, этот же метод может быть расширен для использования с любым из методов массива.

Ответ 4

Я действительно могу представить ситуации, в которых я предпочел бы использовать подход @kontr0l, чем что-то еще, но вы должны понимать, что этот подход квадратичен, поэтому в основном этот код является абстракцией для наивного подхода - перебирает все значения в двух массивах,

Есть подходы лучше, чем квадратичные, я не буду использовать здесь какую-либо большую нотацию O, но здесь есть два основных подхода: оба лучше, чем наивные:

  • итерация через один из массивов и проверка наличия в отсортированном втором массиве с использованием двоичного поиска.
  • поместите значения в set/hash/dictionary/вы назовите его.

Как уже упоминалось, первый подход может быть принят для объектов, если вы переопределяете стандартный метод difference с использованием более гибкого аналога метода indexOf.

При втором подходе мы можем поразить стену тем, что по состоянию на февраль 2010 года только современные браузеры поддерживают Устанавливает. Что касается хэшей (ну, объектов) в javascript, они могут иметь только строковые ключи, поэтому любой объект, вызываемый как ключ, сначала преобразуется через метод toString. Итак, мы должны предоставить некоторые = > соответствия. На практике в большинстве случаев это довольно просто, например, для вашего конкретного примера такое соответствие может быть просто String(obj.id).

Имея такое соответствие, мы также можем использовать следующий подход lodas/undercore:

var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');

// actually here we can stop in some cases, because 
// quite often we need to identify object, but not the object itself - 
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);

//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:

var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})

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

Трюк заключается в реализации набора путем введения двух вспомогательных массивов следующим образом:

var naturalSet = function (arr) {
    var sparse = [];
    var dense = [];

    var contains = function (i) {
        var res = sparse[i] < dense.length && dense[sparse[i]] == i;
        return res;
    }

    var add = function (v) {
        if (!contains(v)) {
            sparse[v] = dense.length;
            dense.push(v);
        }
    }

    arr.forEach(add);

    return {
        contains: contains,
        toArray: function () {
            return dense
        },
        _getDense: function () {
            return dense
        },
        _getSparse: function () {
            return sparse
        }
    }
}

Тогда мы можем ввести множество с отображением в naturalSet:

var set = function (arr, valueOf) {
    var natSet = naturalSet(arr.map(valueOf));
    return {
        contains: function (item) {
            return natSet.contains(valueOf(item))
        },
        toArray: function () {
            var sparse = natSet._getSparse();
            var res = natSet._getDense().map(function (i) {
                return arr[sparse[i]];
            });
            return res;
        }
    }
}

и, наконец, мы можем ввести пересечение:

var intersection = function(arr1, arr2, valueOf) {
   return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}

Таким образом, опираясь на структуру данных, которые вы работаете, иногда может вам помочь.

Ответ 5

without using underscorejs,
here is the pretty simple method i got solution ... 

a = [{'key':'123'},{'key':'222'},{'key':'333'}]
b = [{'key':'123'},{'key':'222'}]

var diff = a.filter(function(item1) {
  for (var i in b) {
    if (item1.key === b[i].key) { return false; }
  };
  return true;
});
console.log('result',diff)

Ответ 6

Простите меня, чтобы прыгать в конце здесь, но это может помочь:

array_of_objects = 
    // return the non-matching items (without the expected properties)
    _.difference(array_of_objects,
        // filter original list for items with expected properties
        _.where(
            // original list
            array_of_objects,
            // expected properties
            {'id':1, 'value':10}
        )
    )