Created
February 14, 2020 21:57
-
-
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
This file contains 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
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