0

My question is slightly similar to this one.

So, say I have an object like this:

var obj = {
  'a': {
    'b': {
      'c': 1,
      'd': 2
    },
    'e': 3
  },
  'f': 4,
  'g': 5
};

I want to run that through a function and create an array that looks something like this:

var arr =
  'a',
  'a.b',
  'a.b.c',
  'a.b.d',
  'a.e',
  'f',
  'g'
];

The purpose of that array is so that I can later loop through objects with the same hierarchial format in the same exact same way. I'm unsure about how to do this step.

So, given obj and arr, it would use a for loop to access all the key-value pairs in obj. As far as I know, you can't access a nested property like this: obj['a.b.c'], so I'm not exactly sure how to do this.

Clarification Edit:

After this array is created, I'm wondering how to use it to loop through objects of the same format in the way described by the array. For example

function iterateAnotherObjectWithSameFormat(aObj) {
  for (var i = 0; i < arr.length; i++) {
    // access aObj['a'], then aObj['a.b'], then aObj['a.b.c'], etc..
  }
}
Community
  • 1
  • 1
Chron Bag
  • 587
  • 4
  • 17
  • Sorry is your question how to create and populate `arr`, or how to access the key-value pairs in `obj`, or both? – Crescent Fresh Jul 11 '16 at 15:23
  • @CrescentFresh: My question is firstly, how to create that array that designates looping order for the object, and then secondly, using that array to loop through objects in the order prescribed by that created array. – Chron Bag Jul 11 '16 at 15:24

2 Answers2

2

You could take a recursive approach where a single call iterates over the properties and put the keys into an array. Thn call the function again with the actual value and the array with the visited keys until no other object is found.

function flatKeys(object) {

    function iter(part, keys) {
        Object.keys(part).forEach(function (k) {
            var allKeys = keys.concat(k);
            flat.push(allKeys.join('.'));
            if (part[k] !== null && !Array.isArray(part[k]) && typeof part[k] === 'object') {
                iter(part[k], allKeys);
            }
        });
    }

    var flat = [];
    iter(object, []);
    return flat;
}

function getValue(object, path) {
    return path.split('.').reduce(function (r, a) {
        return (r || {})[a];
    }, object);
}

var object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4, 'g': 5 },
    keys = flatKeys(object)

console.log(keys);
keys.forEach(function (a) {
    console.log(a, getValue(object, a));
});

Flat object

function flatKeys(object) {

    function iter(part, keys) {
        Object.keys(part).forEach(function (k) {
            var allKeys = keys.concat(k);
            flat[keys.concat(k).join('.')] = part[k];
            if (part[k] !== null && !Array.isArray(part[k]) && typeof part[k] === 'object') {
                iter(part[k], keys.concat(k));
            }
        });
    }

    var flat = {};
    iter(object, []);
    return flat;
}

var object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4, 'g': 5 },
    flat = flatKeys(object);

console.log(flat);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • I think I'm missing something. I see how this creates the array of hierarchical keys, but how do I later use that array to loop through objects with the same format? – Chron Bag Jul 11 '16 at 15:23
  • you need an object with the joined keys as property, right? – Nina Scholz Jul 11 '16 at 15:24
  • You can do it in this way: `flatKeys(object).forEach(k=>{ var prop = k.split('.').reduce((a, b)=>a[b], object); console.log(prop); });` – Jose Hermosilla Rodrigo Jul 11 '16 at 15:35
  • @JoseHermosillaRodrigo: Hmm, is there any way to put that in the array building code instead (like have each array index return a function that given the object yields the value at that position)? Some of these objects are extremely long and we have to process a ton of them, so accessing has be very quick, but the initial array building can be slow. – Chron Bag Jul 11 '16 at 15:36
  • yes, i could, but i don't know if es6 and i would keep a function for getting a single result. – Nina Scholz Jul 11 '16 at 15:36
  • @ChronBag, you could get a flat object with the content of the nested objects. – Nina Scholz Jul 11 '16 at 15:50
  • @NinaScholz: Turn it into a flat object... hm yeah that might work. – Chron Bag Jul 11 '16 at 15:54
  • @ChronBag, do you need the reference to an object, like `a`? – Nina Scholz Jul 11 '16 at 16:01
  • @NinaScholz: Yeah. The function should later be able to get the values at each key by supplying only the root object and the built array. – Chron Bag Jul 11 '16 at 16:06
0

Each property name encountered will be prefixed with keys up the property chain and added to an array.

Searching continues until all nested objects are processed.

function findKeys(object, prefix) {
  prefix = (typeof prefix !== 'undefined') ? (prefix + '.') : '';

  var keys = [];
  Object.keys(object).forEach(function(key) {
    keys.push(prefix + key);
    if (typeof object[key] === 'object' && object[key] !== null && !Array.isArray(object[key]))
      keys = keys.concat(findKeys(object[key], prefix + key));
  });
  return keys;
};


console.log( findKeys({ 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4, 'g': 5 }) );

If you wish to access an object property with a variable name, you must use the bracket notation.

But you can't access nested properties with a string. Regardless of dots, your string is treated as a normal property name.

You need to parse the string yourself. Luckily, it's very simple with reduce.

var object = {
  'a': {
    'b': 5
  },
  'a.b': 10
};

var property = 'a.b';
console.log(object[property]);                            // 10
console.log(getNestedPropertyValue(object, property));    // 5

function getNestedPropertyValue(object, nestedProperty) {
  return nestedProperty.split('.').reduce(function(object, property) {
    return object[property];
  }, object);
}

Here is a complete example:

function findKeys(object, prefix) {
  prefix = (typeof prefix !== 'undefined') ? (prefix + '.') : '';

  var keys = [];
  Object.keys(object).forEach(function(key) {
    keys.push(prefix + key);
    if (typeof object[key] === 'object' && object[key] !== null && !Array.isArray(object[key]))
      keys = keys.concat(findKeys(object[key], prefix + key));
  });
  return keys;
};


var obj1 = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4, 'g': 5 };

var obj2 = { 'a': { 'b': { 'c': 111, 'd': 222 }, 'e': 333 }, 'f': 444, 'g': 555 };

var arr = findKeys(obj1);


var iterateAnotherObjectWithSameFormat = (function() {

  function getNestedPropertyValue(object, nestedProperty) {
    return nestedProperty.split('.').reduce(function(object, property) {
      return object[property];
    }, object);
  }

  return function(object, keys, callback) {
    keys.forEach(function(key) {
      callback(getNestedPropertyValue(object, key));
    });
  };

}());

iterateAnotherObjectWithSameFormat(obj2, arr, function(value) {
  console.log(value);
});
pishpish
  • 2,574
  • 15
  • 22