Я приймаю дещо більш загальний підхід, хоча подібний за ідеями до підходів як @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"}