So I'm working on a project that compares different arrays. It's almost completely working, however I have one problem. Say that in this situation, a and b are two different fetches that hold array data. The data is mostly the same however one is slightly more updated than the other (the non-updated one is a subset essentially). The code I have down here is this:
async function dataDetect() {
let fetchA = await fetch('https://a/data');
let fetchB = await fetch('https://b/data');
let dataA = await fetchA.json();
let dataB = await fetchB.json();
let differenceDetector = (dataA, dataB) => {
let compareA = new Set(dataA);
for (const x of new Set(dataB)) {
if (compareA.has(x)) {
compareA.delete(x);
} else {
compareA.add(x);
}
}
return Array.from(compareA);
};
let detected = differenceDetector(dataA, dataB);
console.log('All differences script detected: ',{detected});
}
dataDetect();
And almost everything is working. However, I'm having a huge problem. For some reason whenever I run this, the total array is actually both of the arrays combined, and it never removed the common elements. I'm sure there is a way to fix this but I've tried multiple combinations as to how and none of them have fully worked. My problem is kinda like this(these aren't the actual arrays in my thing):
dataA=['Violet','Orange','Plumage','Crimson']
and
dataB=['Violet','Orange','Plumage','Crimson','Maroon']
.
The console logs this: ['Violet','Orange','Plumage','Crimson','Violet','Orange','Plumage','Crimson','Maroon']
.
My final log is literally just both of the arrays stacked. This works with normal arrays but with fetches it doesn't. Why does this happen and can someone explain how to fix this?
let differenceDetector = (dataA, dataB) => {
let compareA = new Set(dataA);
for (const x of new Set(dataB)) {
if (compareA.has(x)) {
compareA.delete(x);
} else {
compareA.add(x);
}
}
return Array.from(compareA);
};
const dataB = ['Violet', 'Orange', 'Plumage', 'Crimson', 'Maroon'];
const dataA = ['Violet', 'Orange', 'Plumage', 'Crimson'];
console.log(differenceDetector(dataA, dataB));
CodePudding user response:
The distinction between arrays of objects and strings has to do with how equality is tested. The Set has()
method wraps an equality test (like ===
) which works for immutable types but requires generalization to compare objects, such as those the OP might get from an API...
This intersection function allows the caller to pass in an arbitrary predicate. The caller can use it to perform an equivalence test on objects.
function differenceDetector(arrayA, arrayB, test) {
test = test || ((a, b) => a === b); // default to deep equality
return arrayA.filter(a => !arrayB.find(b => test(a,b)));
}
// this works as expected for strings...
const a = ['Violet', 'Orange', 'Plumage', 'Crimson', 'Maroon'];
const b = ['Violet', 'Orange', 'Plumage', 'Crimson'];
console.log(differenceDetector(a, b));
// and it also works for objects...
const c = [{ id: 1 }, { id: 2}, { id: 3 }, { id: 4 }];
const d = [{ id: 1 }, { id: 2}, { id: 3 }];
const test = (a, b) => a.id === b.id;
// note that we pass an equivalence predicate "test"
// without it, the default "===" test will give the OP's undesired result
console.log(differenceDetector(c, d, test));
Note that this implementation, like the OP's, is not commutative. The difference found is elements of the first array that are not in the second, according to a given equality test.
Also note, comparing whole objects for equivalence (all keys and values are equivalent) is a tricky subject. A cheap way to code -- though maybe not so cheap at run time -- is to compare JSON encodings...
const wholeObjectTest = (a, b) => JSON.stringify(a) === JSON.stringify(b);