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..
  }
}
2
  • Sorry is your question how to create and populate arr, or how to access the key-value pairs in obj, or both? Commented Jul 11, 2016 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. Commented Jul 11, 2016 at 15:24

2 Answers 2

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);

Sign up to request clarification or add additional context in comments.

9 Comments

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?
you need an object with the joined keys as property, right?
You can do it in this way: flatKeys(object).forEach(k=>{ var prop = k.split('.').reduce((a, b)=>a[b], object); console.log(prop); });
@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.
yes, i could, but i don't know if es6 and i would keep a function for getting a single result.
|
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);
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.