0

Objective:

I am developing an app using AngularJS and Lodash. I need a function that do the following transformation:

From

{
 a: 0,
 b: {x: {y: 1, z: 2}},
 c: 3
}

into

{
 a: 0,
 'b.x.y': 1,
 'b.x.z': 2,
 c: 3
}

Right now, I am using a recursive function to traverse through the object. For the time being, my code looks like that (incomplete):

            var obj = {},
                keys, property, value;

            var recur = function (mat) {

                keys = Object.keys(mat);

                for (var i = 0; i < keys.length; i++) {
                    property = keys[i];
                    value = mat[property];

                    if (isObj(value)) {
                        //debugger;
                        recur(value);


                    } else {
                        obj[property] = value;
                    }
                }
            }
            recur({
             a: 0,
             b: {x: {y: 1, z: 2}},
             c: 3
            });
            
            console.log(obj);
            
            function isObj(value) {
            return typeof value === "object";
        }

One issue that I am getting while debugging is:

i is not being incremented; during the highest-level recursion call, i remains 0.

Anyone knows why? And if you know of any other way for doing this transformation, please share it.

Kind Regards.

Ramsing Nadeem
  • 101
  • 1
  • 12

4 Answers4

2

It's because keys is declared in the wrong scope. It should be declared inside recur. Since it isn't, when recur recurses, keys gets overwritten.

That doesn't make i not increase, but it does mean your loops will terminate at surprising times because keys changes when you recurse.

property and value should also be declared within recur; in general, declare variables in the innermost scope you can. (temp need not be declared at all, as you never use it.)

To get your end result, obviously you have to change more than just that, but you said the code is incomplete. This is the problem with the loops not working properly.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks; shall continue working on the transformation; always thought it was better to remove "var" out of loops and/or recursions for better performance (but yeah, behavior might often become abnormal). – Ramsing Nadeem Sep 13 '17 at 08:00
2

For converting a valid object you could use an iterative and recursive approach for getting values and their pathes.

function convert(object) {
    function iter(o, p) {
        Object.keys(o).forEach(function (k) {
            var q = p.concat(k);
            if (o[k] && typeof o[k] === 'object') {
                iter(o[k], q);
                return;
            }
            object[q.join('.')] = o[k];
        });
    }

    Object.keys(object).forEach(function (k) {
        if (object[k] && typeof object[k] === 'object') {
            iter(object[k], [k]);
            delete object[k];
        }
    });
}

var object = { a: 0, b: { x: { y: 1, z: 2 } }, c: 3 };

convert(object);

console.log(object);

Single iterator/new result

function convert(object) {
    function iter(o, p) {
        p = p ? p + '.' : '';
        Object.keys(o).forEach(function (k) {
            if (o[k] && typeof o[k] === 'object') {
                iter(o[k], p + k);
                return;
            }
            result[p + k] = o[k];
        });
    }

    var result = {};
    iter(object);
    return result;
}

var object = { a: 0, b: { x: { y: 1, z: 2 } }, c: 3 };

console.log(convert(object));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

you can do it in the following way

let obj = {
 a: 0,
 b: {x: {y: 1, z: 2}},
 c: 3
}

let result = {};
function rec(obj, parent){
   
   if(typeof obj == 'object'){
      for(let property in obj){
      
          rec(obj[property], (parent == '' ? parent:parent+'.')+property);
      }
   }
   else {
      result[parent] = obj;
   }
}

rec(obj, '');
console.log(result);
marvel308
  • 10,288
  • 1
  • 21
  • 32
0

Thanks for the help, everyone (particularly to @T.J. Crowder for pointing out the error I've made related to scope).

My complete code (any suggestion - related to good/bad practices, etc - would be welcome):

var recur = function (obj, mat, parent) {

             var keys = Object.keys(mat);

             for (var i = 0; i < keys.length; i++) {
                    var property = keys[i];
                    var value = mat[property];

                    if (isObj(value)) {
                        recur(obj, value, parent ? parent + "." + property : property);

                    } else {
                        obj[parent ? parent + "." + property : property] = value;
                    } 
            }
            return obj;
}
            console.log(recur({}, {
             a: 0,
             b: {x: {y: 1, z: 2}},
             c: 3
            }));
            
            function isObj(value) {
            return typeof value === "object";
}
Ramsing Nadeem
  • 101
  • 1
  • 12