Skip to content

Instantly share code, notes, and snippets.

@davidrautert
Last active November 13, 2018 20:40
Show Gist options
  • Save davidrautert/a42b97cf3437ff2c918ecb589b906d39 to your computer and use it in GitHub Desktop.
Save davidrautert/a42b97cf3437ff2c918ecb589b906d39 to your computer and use it in GitHub Desktop.
Recursive Unique object filtering
// I'm writing out my whole thought process for this. Lots of console.logs and comments
// Let's make some data
let objA = { something: '1 does a thing' };
let objB = { something: '2 does a thing' };
let objC = objA;
let objD = { something: objC };
let arrA = ['test', objB, 'testing thing', objA, '3 doing things'];
let data = [objA, objB, objC, objD, arrA];
// Objects are compared by reference, not value so it suits this particular problem well
//console.log(objA == objB); // false
//console.log(objA == objC); // true
//console.log(objB == objC); // false
//console.log(objD.something == objA); // true
// Simple, removes all duplicate objects at the highest level
// But it doesn't perform well on larger arrays because indexOf is lame and slow
// And it doesn't do any recursion to find nested objects, like objD's or arrA's use case
function removeDuplicateObjects(data) {
return data.filter(function(datum, i, self){
return self.indexOf(datum) == i;
});
}
// I saw Javascript Set a while ago and wanted to see if it would be useful here
// the idea is to iterate through the tree and add object references to a set.
// in theory, it should only be adding the references and not the values..?
const testSet = new Set();
testSet.add(objA).add(objB).add(objC).add(objD); // chaining! neat
console.log('Does the set have objA?', testSet.has(objA));
console.log('Does the set have objC?', testSet.has(objC));
console.log('But does it really?');
for(let item of testSet){
console.log(item);
}
// It doesn't, but it is checking by reference for the object existing in the set
// so this *should* work. Let's iterate down the object trees, check if the object
// exists in the set and prune it from the tree if so, otherwise add to set and continue
function removeDuplicateObjectsRecursive(arr) {
const objSet = new Set();
function isDuplicate(item) {
if(objSet.has(item) && typeof item == 'object' && item != null) {
return true
}
return false;
}
function isObject(item) {
if(typeof item == 'object' && item != null) {
return true;
}
return false;
}
function removeDuplicates(data) {
if(Array.isArray(data)) {
for(let item of data) {
if(isObject(item)) {
if(isDuplicate(item)) {
data.splice(data.indexOf(item), 1);
} else {
objSet.add(item);
removeDuplicates(item);
}
}
}
} else if(isObject(data)) {
for(var item in data) {
if(isObject(data[item])) {
if(!objSet.has(data[item])) {
objSet.add(data[item]);
} else {
delete data[item];
}
}
}
}
return data;
}
return removeDuplicates(arr);
}
// there's definitely a more elegant way to write this i think
console.log('Original Data: ', data);
//console.log('removeDuplicateObjects: ', removeDuplicateObjects(data));
console.log('removeDuplicateObjectsRecursive: ', removeDuplicateObjectsRecursive(data));
// I see problems with this method. The recursion splices things in the
// loop so I should be iterating in reverse. But if I do that then I'll be
// splicing duplicates in reverse too, which means objA gets spliced, not objC
// I don't want to have to traverse the tree twice. An easy fix would be resetting
// the index in the for loop, but that's wonky and not sure how to do that or
// if I even should.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment