smart-circular.js
function(object, customizer) {

    var foundStack = [], //Stack to keep track of discovered objects
        queueOfModifiers = [], //Necessary to change our JSON as we take elements from the queue (BFS algorithm)
        queue = []; //queue of JSON elements, following the BFS algorithm

    //We instantiate our result root.
    var result = _.isArray(object) ? [] : {};

    //We first put all the JSON source in our queues
    queue.push(object);
    queueOfModifiers.push(new ObjectEditor(object, ""));

    var positionStack;
    var nextInsertion;

    //BFS algorithm
    while(queue.length > 0) {

        //JSON to be modified and its editor
        var value = queue.shift();
        var editor = queueOfModifiers.shift();
        //The path that leads to this JSON, so we can build other paths from it
        var path = editor.path;

        //We first attempt to make any personalized replacements
        //If customizer doesn't affect the value, customizer(value) returns undefined and we jump this if
        if(customizer !== undefined) {

            //By using this variable, customizer(value) is called only once.
            var customizedValue = customizer(value);

            if(customizedValue !== undefined) value = customizedValue;
        }


        if(typeof value === "object") {

            positionStack = _.chain(foundStack)
                .map("value")
                .indexOf(value)
                .value();

            //If the value has already been discovered, we only fix its circular reference
            if(positionStack !== -1) {
                nextInsertion = foundStack[positionStack].makePathName();
            }
            else {
                //At the first time we discover a certain value, we put it in the stack
                foundStack.push(new FoundObject(value, path));

                nextInsertion = value;

                for(var component in value) {
                    if(_.has(value, component)) {
                        queue.push(value[component]);
                        var newPath = path + "[" + component + "]";
                        queueOfModifiers.push(new ObjectEditor(result, newPath));
                    }
                }
            }
        }
        //If it's an elementary value, it can't be circular, so we just put this value in our JSON result.
        else {
            nextInsertion = value;
        }

        editor.editObject(nextInsertion);
    }

    return result;
}
function(object, customizer) {

    var foundStack = [], //Stack to keep track of discovered objects
        queueOfModifiers = [], //Necessary to change our JSON as we take elements from the queue (BFS algorithm)
        queue = []; //queue of JSON elements, following the BFS algorithm

    //We instantiate our result root.
    var result = _.isArray(object) ? [] : {};

    //We first put all the JSON source in our queues
    queue.push(object);
    queueOfModifiers.push(new ObjectEditor(object, ""));

    var positionStack;
    var nextInsertion;

    //BFS algorithm
    while(queue.length > 0) {

        //JSON to be modified and its editor
        var value = queue.shift();
        var editor = queueOfModifiers.shift();
        //The path that leads to this JSON, so we can build other paths from it
        var path = editor.path;

        //We first attempt to make any personalized replacements
        //If customizer doesn't affect the value, customizer(value) returns undefined and we jump this if
        if(customizer !== undefined) {

            //By using this variable, customizer(value) is called only once.
            var customizedValue = customizer(value);

            if(customizedValue !== undefined) value = customizedValue;
        }


        if(typeof value === "object") {

            positionStack = _.chain(foundStack)
                .map("value")
                .indexOf(value)
                .value();

            //If the value has already been discovered, we only fix its circular reference
            if(positionStack !== -1) {
                nextInsertion = foundStack[positionStack].makePathName();
            }
            else {
                //At the first time we discover a certain value, we put it in the stack
                foundStack.push(new FoundObject(value, path));

                nextInsertion = value;

                for(var component in value) {
                    if(_.has(value, component)) {
                        queue.push(value[component]);
                        var newPath = path + "[" + component + "]";
                        queueOfModifiers.push(new ObjectEditor(result, newPath));
                    }
                }
            }
        }
        //If it's an elementary value, it can't be circular, so we just put this value in our JSON result.
        else {
            nextInsertion = value;
        }

        editor.editObject(nextInsertion);
    }

    return result;
}
function ( object , customizer ) { var foundStack = [ ] , //Stack to keep track of discovered objects queueOfModifiers = [ ] , //Necessary to change our JSON as we take elements from the queue (BFS algorithm) queue = [ ] ; //queue of JSON elements, following the BFS algorithm //We instantiate our result root. var result = _ . isArray ( object ) ? [ ] : { } ; //We first put all the JSON source in our queues queue . push ( object ) ; queueOfModifiers . push ( new ObjectEditor ( object , "" ) ) ; var positionStack ; var nextInsertion ; //BFS algorithm while ( queue . length > 0 ) { //JSON to be modified and its editor var value = queue . shift ( ) ; var editor = queueOfModifiers . shift ( ) ; //The path that leads to this JSON, so we can build other paths from it var path = editor . path ; //We first attempt to make any personalized replacements //If customizer doesn't affect the value, customizer(value) returns undefined and we jump this if if ( customizer !== undefined ) { //By using this variable, customizer(value) is called only once. var customizedValue = customizer ( value ) ; if ( customizedValue !== undefined ) value = customizedValue ; } if ( typeof value === "object" ) { positionStack = _ . chain ( foundStack ) . map ( "value" ) . indexOf ( value ) . value ( ) ; //If the value has already been discovered, we only fix its circular reference if ( positionStack !== - 1 ) { nextInsertion = foundStack [ positionStack ] . makePathName ( ) ; } else { //At the first time we discover a certain value, we put it in the stack foundStack . push ( new FoundObject ( value , path ) ) ; nextInsertion = value ; for ( var component in value ) { if ( _ . has ( value , component ) ) { queue . push ( value [ component ] ) ; var newPath = path + "[" + component + "]" ; queueOfModifiers . push ( new ObjectEditor ( result , newPath ) ) ; } } } } //If it's an elementary value, it can't be circular, so we just put this value in our JSON result. else { nextInsertion = value ; } editor . editObject ( nextInsertion ) ; } return result ; }
Main function to travel through the JSON and transform the circular references and personalized replacements
Main function to travel through the JSON and transform the circular references and personalized replacements