Przyjmuję nieco bardziej ogólne podejście, chociaż pomysły są podobne do podejść @Cerbrus i @Kasper Moerch . Tworzę funkcję, która przyjmuje predykat, aby określić, czy dwa obiekty są równe (tutaj ignorujemy tę $$hashKey
właściwość, ale może to być cokolwiek) i zwracam funkcję, która oblicza symetryczną różnicę dwóch list na podstawie tego predykatu:
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"}
Ma jedną niewielką przewagę nad podejściem Cerebrusa (podobnie jak podejście Kaspera Moercha), ponieważ ucieka wcześnie; jeśli znajdzie dopasowanie, nie przejmuje się sprawdzaniem reszty listy. Gdybym miał pod curry
ręką funkcję, zrobiłbym to trochę inaczej, ale to działa dobrze.
Wyjaśnienie
W komentarzu prosiliśmy o bardziej szczegółowe wyjaśnienie dla początkujących. Oto próba.
Przekazujemy następującą funkcję do makeSymmDiffFunc
:
function(x, y) {
return x.value === y.value && x.display === y.display;
}
Ta funkcja pozwala stwierdzić, że dwa obiekty są równe. Podobnie jak wszystkie funkcje, które zwracają wartość true
lub false
, można ją nazwać „funkcją predykatu”, ale to tylko terminologia. Najważniejsze jest to, że makeSymmDiffFunc
jest skonfigurowana z funkcją, która przyjmuje dwa obiekty i zwraca, true
jeśli uznamy je za równe, false
jeśli nie.
Używając tego, makeSymmDiffFunc
(czytaj "funkcja tworzenia symetrycznej różnicy") zwraca nam nową funkcję:
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
To jest funkcja, której będziemy faktycznie używać. Przekazujemy mu dwie listy i znajduje elementy w pierwszej, a nie w drugiej, a następnie elementy w drugiej nie na pierwszej i łączy te dwie listy.
Jednak patrząc jeszcze raz, zdecydowanie mogłem wziąć wskazówkę z twojego kodu i całkiem uprościć główną funkcję, używając 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
używa predykatu i zwraca elementy swojej pierwszej listy, a nie drugiej. To jest prostsze niż mój pierwszy przebieg z oddzielną contains
funkcją.
Wreszcie funkcja główna jest opakowana w natychmiast wywołane wyrażenie funkcyjne ( IIFE ), aby utrzymać funkcję wewnętrzną complement
poza zasięgiem globalnym.
Aktualizacja, kilka lat później
Teraz, gdy ES2015 stał się całkiem nieźle wszechobecny, sugerowałbym tę samą technikę, z dużo mniejszą liczbą schematów:
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"}