Created
May 6, 2020 16:56
-
-
Save Xunnamius/57e12bf40e373fd640ed44e29b618744 to your computer and use it in GitHub Desktop.
Circular JSON Implementation: `uncirc` and `recirc` for stringifying/parsing circular objects in JavaScript
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
// Takes an object and replaces the circular references (including deep circular references) with strings | |
var uncirc = function(obj, ident, seen) | |
{ | |
seen = seen || {}; | |
// Push the object identity (key) and object (value) onto the "seen" list | |
seen[ident] = obj; | |
// Returns the ident of a value or null if we have not yet seen it | |
// Could have made this an anonymous function | |
function whatsTheIdentOf(value) | |
{ | |
for(var key in seen) | |
{ | |
if(seen.hasOwnProperty(key) && seen[key] === value) | |
return key; | |
} | |
return null; | |
} | |
// Iterate (deep) over the target object | |
for(var key in obj) | |
{ | |
// Do not iterate up the prototype chain, please JavaScript... -_- | |
if(obj.hasOwnProperty(key)) | |
{ | |
var value = obj[key]; | |
var seenIdent = whatsTheIdentOf(value); | |
// Have we seen this value before? | |
if(seenIdent !== null) | |
obj[key] = seenIdent; | |
// Catches arrays AND typical objects that have not been seen already | |
else if(typeof(value) == 'object' && value !== null) | |
uncirc(obj[key], ident + '.' + key, seen); | |
} | |
} | |
// Not really necessary since obj was passed by reference, but passing it back makes for a nice fluent interface | |
return obj; | |
}; | |
// Takes an object with strings representing circular references and returns it to its original state | |
var recirc = function(obj, ident, rootObj) | |
{ | |
rootObj = rootObj || obj; | |
// Takes a string and uses it to traverse an object | |
// For example: | |
// obj = { b: { c: 5 }}; can be traversed with the string 'b.c' which will | |
// return the integer 5 | |
var crawlObjWithString = function(str, objkt) | |
{ | |
objkt = objkt || obj; | |
var strSplit = str.split('.'); | |
var specialKey = strSplit.shift(); | |
var value = objkt[specialKey]; | |
if(strSplit.length && typeof(value) == 'object' && value !== null) | |
return crawlObjWithString(strSplit.join('.'), objkt[specialKey]); | |
else | |
// Why not just return value instead? I wonder... | |
return objkt[specialKey]; | |
}; | |
for(var key in obj) | |
{ | |
if(obj.hasOwnProperty(key)) | |
{ | |
var value = obj[key]; | |
// Is this one of our special string values from uncirc?! | |
if(typeof(value) == 'string' && value.indexOf(ident) === 0) | |
obj[key] = value == ident ? rootObj : crawlObjWithString(value.substr(ident.length + 1), obj); | |
else if(typeof(value) == 'object' && value !== null) | |
recirc(obj[key], ident, obj); | |
} | |
} | |
// Not really necessary since obj was passed by reference, but passing it back makes for a nice fluent interface | |
return obj; | |
}; | |
// How to use these two functions | |
var a = { b:1, c:2, d:3 }; | |
a['e'] = a; | |
a['f'] = { a:1, b:2, c:4, d:8, e:16, f:32, g: { z: a }}; | |
a.f.g.x = { wtf: a.f }; | |
console.log(JSON.stringify(uncirc(a, 'LOL'))); | |
console.log(recirc(a, 'LOL')); | |
// Play with it in node js on the terminal (naming this file `nodefun.js`): | |
// $ node | |
// > .load nodefun.js |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment