Skip to content

Instantly share code, notes, and snippets.

@sploders101
Created February 14, 2020 21:57
Show Gist options
  • Save sploders101/1f245d76c34ec94e4d94a1fff9581242 to your computer and use it in GitHub Desktop.
Save sploders101/1f245d76c34ec94e4d94a1fff9581242 to your computer and use it in GitHub Desktop.
[JS] Circular reference deconstructor/reconstructor. Can be used to serialize objects with circular references
const rand = () => Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
type CSValue = CSLiteral | CSRef;
type CSLiteral = string | number | boolean | null;
type CSRef = [string];
export function analyzeRefs(obj: any) {
const reference = new Map<object | any[], string>();
// Build reference table
JSON.stringify(obj, (key, value) => {
if(typeof(value) === "object" && value !== null) {
if(reference.has(value)) {
return null;
} else {
reference.set(value, value === obj ? "entry" : rand());
return value;
}
} else return null;
});
const deCircularized: Record<string, Record<string, CSValue> | CSValue[]> = {};
for(const [value, key] of reference) {
if(Array.isArray(value)) {
const thisObj: CSValue[] = value.map((arrItem) => {
if(typeof(arrItem) === "object") {
return [reference.get(arrItem)!];
} else {
return arrItem;
}
});
deCircularized[key] = thisObj;
} else {
const thisObj: Record<string, CSValue> = {};
for(const entry of Object.entries(value)) {
if(typeof(entry[1]) === "object") {
thisObj[entry[0]] = [reference.get(entry[1])!];
} else {
thisObj[entry[0]] = entry[1];
}
}
deCircularized[key] = thisObj;
}
}
return deCircularized;
}
export function reconstructRefs(deCircularized: Record<string, Record<string, CSValue> | CSValue[]>) {
const reconstructedRefs = new Map<string, object | any[]>();
const entries = Object.entries(deCircularized);
// Create empty variables first, to allow for circular nesting upon population
for(const [key, serialized] of entries) {
reconstructedRefs.set(key, Array.isArray(serialized) ? [] : {});
}
// Populate
for(const [key, serialized] of entries) {
// Get current referenced object
const newVar = reconstructedRefs.get(key)!;
if(Array.isArray(serialized) && Array.isArray(newVar)) {
newVar.push(...serialized.map((ptr) => {
if(typeof(ptr) !== "object" || ptr === null) {
return ptr;
} else {
return reconstructedRefs.get(ptr[0]);
}
}));
} else {
for(const entry of Object.entries(serialized)) {
if(typeof(entry[1]) !== "object" || entry[1] === null) {
newVar[entry[0]] = entry[1];
} else {
newVar[entry[0]] = reconstructedRefs.get(entry[1][0]);
}
}
}
}
return reconstructedRefs.get("entry");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment