Используя метод "разности" подчёркивания на массивах объектов
_.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}];
похоже, не работает
Ответы
Ответ 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}
)
)