Я придерживаюсь чуть более универсального подхода, хотя по идее он похож на подходы @Cerbrus и @Kasper Moerch . Я создаю функцию, которая принимает предикат, чтобы определить, равны ли два объекта (здесь мы игнорируем $$hashKeyсвойство, но это может быть что угодно) и возвращаю функцию, которая вычисляет симметричную разность двух списков на основе этого предиката:
a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]
var makeSymmDiffFunc = (function() {
var contains = function(pred, a, list) {
var idx = -1, len = list.length;
while (++idx < len) {if (pred(a, list[idx])) {return true;}}
return false;
};
var complement = function(pred, a, b) {
return a.filter(function(elem) {return !contains(pred, elem, b);});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
var myDiff = makeSymmDiffFunc(function(x, y) {
return x.value === y.value && x.display === y.display;
});
var result = myDiff(a, b); //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
У него есть одно небольшое преимущество перед подходом Церебруса (как и подход Каспера Мёрча) в том, что он ускользает раньше; если он находит совпадение, он не утруждает себя проверкой остальной части списка. Если бы у меня была под curryрукой функция, я бы сделал это немного по-другому, но это нормально работает.
Объяснение
В комментарии для начинающих просили более подробное объяснение. Вот попытка.
Мы передаем следующую функцию makeSymmDiffFunc:
function(x, y) {
return x.value === y.value && x.display === y.display;
}
С помощью этой функции мы решаем, что два объекта равны. Как и все функции, возвращающие trueили false, ее можно назвать «функцией предиката», но это всего лишь терминология. Главное, что makeSymmDiffFuncнастроена функция, которая принимает два объекта и возвращает, trueесли мы считаем их равными, falseесли нет.
Используя это, makeSymmDiffFunc(читайте "сделать симметричную функцию разности") возвращает нам новую функцию:
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
Это функция, которую мы фактически будем использовать. Мы передаем ему два списка, и он находит элементы из первого, а не из второго, затем элементы из второго, а не из первого, и объединяет эти два списка.
Однако, просматривая это еще раз, я определенно мог взять реплику из вашего кода и немного упростить основную функцию, используя some:
var makeSymmDiffFunc = (function() {
var complement = function(pred, a, b) {
return a.filter(function(x) {
return !b.some(function(y) {return pred(x, y);});
});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
complementиспользует предикат и возвращает элементы первого списка, а не второго. Это проще, чем мой первый проход с отдельной containsфункцией.
Наконец, основная функция заключена в выражение немедленно вызываемой функции ( IIFE ), чтобы внутренняя complementфункция не попадала в глобальную область видимости.
Обновление, несколько лет спустя
Теперь, когда ES2015 стал довольно широко распространенным, я бы предложил ту же технику, но с гораздо меньшим количеством шаблонов:
const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y)))
const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a))
const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display)
const result = myDiff(a, b)
//=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}