0

How to enumerate complex javascript object to get property paths to plain values?

In example if object is:

let complex = {
  person: {name: 'mat', age: 31},
  car: {
      model: 'Volsan',
      engine: 'large', 
      doors:[
        { side:'right front', color: 'blue' },
        { side:'left rear', color: 'red' }
      ]
   }
};

and outcome would be like:

complex.person.name
complex.person.age
complex.car.model
complex.car.engine
complex.car.doors[0].side
complex.car.doors[0].color
complex.car.doors[1].side
complex.car.doors[1].color

so that there would be only those values that are "ending the graph"

eg. it would be reverse _.at from lodash: https://lodash.com/docs/4.17.5#at

jackjack
  • 3
  • 1
  • Possible duplicate of https://stackoverflow.com/questions/11922383/access-process-nested-objects-arrays-or-json/11922384#11922384. – Ahmad Mar 06 '18 at 07:29
  • 2
    @Ahmad He's not asking how to access elements, he's asking how to get an array of the paths to all the elements. – Barmar Mar 06 '18 at 07:31
  • So if I understand correct, you want to give a value and find its path? – Rajesh Mar 06 '18 at 07:31
  • 1
    @Rajesh I think he wants to list *all* the paths. – Barmar Mar 06 '18 at 07:31
  • 2
    Use a recursive function that enumerates each level, and if it's an array or object it recurses into it and appends their properties or indexes to the result. – Barmar Mar 06 '18 at 07:32
  • 1
    Adding to @Barmar's comment, use array notation. So instead of `complex.person.name`, use `complex['person']['name']`. This is simpler to implement and does not require any special handling for Arrays vs Objects – Rajesh Mar 06 '18 at 07:34
  • eg, I dont want values like complex.person or complex.car.doors since those are not scalar values and are not graph ends. Sorry, I am bad at presenting the problem. I can only present it by example. – jackjack Mar 06 '18 at 07:41
  • please add what you have tried. – Nina Scholz Mar 06 '18 at 07:54

1 Answers1

0

This recursive solution uses Array.map(), and Object.keys() to add the keys to a base path. Then we flatten the resulting array using Array.concat() and spread:

const complex = {
  person: {name: 'mat', age: 31},
  car: {
      model: 'Volsan',
      engine: 'large', 
      doors:[
        { side:'right front', color: 'blue' },
        { side:'left rear', color: 'red' }
      ]
   }
}

const addDot = (b, s) => `${b}${b && '.'}${s}`
const wrapBrackets = (b, s) => `${b}[${s}]`

const mapPaths = (obj, base = '') => {
  if(typeof obj !== 'object') {
    return base;
  }

  const formatter = Array.isArray(obj) ? wrapBrackets : addDot;

  return [].concat(...Object.keys(obj)
    .map((key) => mapPaths(obj[key], formatter(base, key))))
};

const result = mapPaths(complex)

console.log(result);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • very beautiful solution! I added valueOf() for my purpose: if(typeof (obj.valueOf()) !== 'object') { eg. there might be new Number(1123) as value. – jackjack Mar 07 '18 at 07:08