Last active
November 13, 2018 20:40
-
-
Save davidrautert/a42b97cf3437ff2c918ecb589b906d39 to your computer and use it in GitHub Desktop.
Recursive Unique object filtering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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