Given I have a circular reference in a large JavaScript object
And I try JSON.stringify(problematicObject)
And the browser throws
Most of the other answers only show how to detect that an object-tree has a circular-reference -- they don't tell you how to fix those circular references (ie. replacing the circular-reference values with, eg. undefined).
The below is the function I use to replace all circular-references with undefined:
export const specialTypeHandlers_default = [
// Set and Map are included by default, since JSON.stringify tries (and fails) to serialize them by default
{type: Set, keys: a=>a.keys(), get: (a, key)=>key, delete: (a, key)=>a.delete(key)},
{type: Map, keys: a=>a.keys(), get: (a, key)=>a.get(key), delete: (a, key)=>a.set(key, undefined)},
];
export function RemoveCircularLinks(node, specialTypeHandlers = specialTypeHandlers_default, nodeStack_set = new Set()) {
nodeStack_set.add(node);
const specialHandler = specialTypeHandlers.find(a=>node instanceof a.type);
for (const key of specialHandler ? specialHandler.keys(node) : Object.keys(node)) {
const value = specialHandler ? specialHandler.get(node, key) : node[key];
// if the value is already part of visited-stack, delete the value (and don't tunnel into it)
if (nodeStack_set.has(value)) {
if (specialHandler) specialHandler.delete(node, key);
else node[key] = undefined;
}
// else, tunnel into it, looking for circular-links at deeper levels
else if (typeof value == "object" && value != null) {
RemoveCircularLinks(value, specialTypeHandlers, nodeStack_set);
}
}
nodeStack_set.delete(node);
}
For use with JSON.stringify specifically, simply call the function above prior to the stringification (note that it does mutate the passed-in object):
const objTree = {normalProp: true};
objTree.selfReference = objTree;
RemoveCircularLinks(objTree); // without this line, the JSON.stringify call errors
console.log(JSON.stringify(objTree));