How can an object with mixed-types of nested children be converted into an array in JS?

Suppose I have an object that I’d like to convert into an array of objects, retaining the object keys as properties for each nested object.

In this simple example, any of these would work:

let data = {
  First: 'I am the danger',
  Second: 'I am the one who knocks',
  Third: 'Magnets, Bih!',
};

// Using .map and Object.keys()
let testOne = Object.keys(data).map((key) => ({ title: key, content: data[key] }));
console.log(testOne);

// Using .reduce and Object.keys()
let testTwo = Object.keys(data).reduce((acc, curr) => {
  return [...acc, { title: curr, content: data[curr] }]
}, []);
console.log(testTwo);

// Using a simple loop
let testThree = [];
for(let i in data) {
  testThree.push({ title: i, content: data[i] });
};
console.log(testThree);

Now consider a more complicated example of an object that contains an inconsistent pattern of objects and child-arrays:

let myCurrentObject = {
  Movies: {
    Comedy: {
      TopRanked: [
        {
          id: 99,
          name: 'The Interview',
          path: ['Movies', 'Comedy', 'TopRanked'],
        },
        {
          id: 100,
          name: 'Eurotrip',
          path: ['Movies', 'Comedy', 'TopRanked'],
        },
      ]
    },
    Action: [
      {
        id: 101,
        name: 'We Were Soldiers',
        path: ['Movies', 'Action'],
      },
      {
        id: 102,
        name: 'The Bell',
        path: ['Movies', 'Action'],
      },
    ]
  },
  Life: {
    Financial: {
      Income: [
        {
          id: 103,
          name: 'Making Hundreds',
          path: ['Life', 'Financial', 'Income'],
        },
        {
          id: 104,
          name: 'Making Thousands',
          path: ['Life', 'Financial', 'Income'],
        },
      ]
    },
    MonthlySpent: [
      {
        id: 105,
        name: 'Just a little bit',
        path: ['Life', 'MonthlySpent'],
      },
      {
        id: 106,
        name: 'Living Large',
        path: ['Life', 'MonthlySpent'],
      },
    ]
  }
}
console.log(myCurrentObject);

And I would like to convert it to an array of objects, so the output is like so:

let myDesiredOutput = [
  {
    name: "Movies",
    obj_children: [
      {
        name: "Comedy",
        obj_children: [
          {
            name: "TopRanked",
            obj_children: [
              {
                id: 99,
                name: 'The Interview',
                path: ['Movies', 'Comedy', 'Top'],
              },
              {
                id: 100,
                name: 'Eurotrip',
                path: ['Movies', 'Comedy', 'Top'],
              },
            ],
          },
        ],
      },
      {
        name: "Action",
        obj_children: [
          {
            id: 101,
            name: 'We Were Soldiers',
            path: ['Movies', 'Action'],
          },
          {
            id: 102,
            name: 'The Bell',
            path: ['Movies', 'Action'],
          },
        ]
      }
    ]
  },
  {
    name: "Life",
    obj_children: [
      {
        name: "Financial",
        obj_children: [
          {
            name: "Income",
            obj_children: [
              {
                id: 103,
                name: 'Making Hundreds',
                path: ['Life', 'Financial', 'Income'],
              },
              {
                id: 104,
                name: 'Making Thousands',
                path: ['Life', 'Financial', 'Income'],
              },
            ]
          },
        ]
      },
      {
        name: "MonthlySpent",
        obj_children: [
          {
            id: 105,
            name: 'Just a little bit',
            path: ['Life', 'MonthlySpent'],
          },
          {
            id: 106,
            name: 'Living Large',
            path: ['Life', 'MonthlySpent'],
          },
        ]
      }
    ]
  },
];
console.log(myDesiredOutput);

I am having a hard time reaching the required outcome.
I have tried to check the typeof as I traverse through it, as in the following example, but this fails miserably:

let data = {
  Movies: {
    Comedy: {
      TopRanked: [
        {
          id: 99,
          name: 'The Interview',
          path: ['Movies', 'Comedy', 'TopRanked'],
        },
        {
          id: 100,
          name: 'Eurotrip',
          path: ['Movies', 'Comedy', 'TopRanked'],
        },
      ]
    },
    Action: [
      {
        id: 101,
        name: 'We Were Soldiers',
        path: ['Movies', 'Action'],
      },
      {
        id: 102,
        name: 'The Bell',
        path: ['Movies', 'Action'],
      },
    ]
  },
  Life: {
    Financial: {
      Income: [
        {
          id: 103,
          name: 'Making Hundreds',
          path: ['Life', 'Financial', 'Income'],
        },
        {
          id: 104,
          name: 'Making Thousands',
          path: ['Life', 'Financial', 'Income'],
        },
      ]
    },
    MonthlySpent: [
      {
        id: 105,
        name: 'Just a little bit',
        path: ['Life', 'MonthlySpent'],
      },
      {
        id: 106,
        name: 'Living Large',
        path: ['Life', 'MonthlySpent'],
      },
    ]
  }
}
function prepareTreeData(data) {
  return Object.entries(data).map(([name, value]) =>
    Object.assign(
      { name },
      name && typeof value === "object"
        ? { value: "", obj_children: this.prepareTreeData(value) }
        : { value, obj_children: [] }
    )
  );
}
console.log(prepareTreeData(data));

Having spent quite a few hours researching and trying to solve this with no success, would appreciate a suggestion on the proper way to achieve this. Tnx.

4 thoughts on “How can an object with mixed-types of nested children be converted into an array in JS?”

  1. You can use recursion for this, and Array.isArray to detect that you have arrived at the deepest level:

    function translate(obj) {
        if (Array.isArray(obj)) return obj;
        return Object.keys(obj).map(key => 
             ({ name: key, obj_children: translate(obj[key]) })
        );
    }
    
    let myCurrentObject = {Movies: {Comedy: {TopRanked: [{id: 99,name: 'The Interview',path: ['Movies', 'Comedy', 'TopRanked'],},{id: 100,name: 'Eurotrip',path: ['Movies', 'Comedy', 'TopRanked'],},]},Action: [{id: 101,name: 'We Were Soldiers',path: ['Movies', 'Action'],},{id: 102,name: 'The Bell',path: ['Movies', 'Action'],},]},Life: {Financial: {Income: [{id: 103,name: 'Making Hundreds',path: ['Life', 'Financial', 'Income'],},{id: 104,name: 'Making Thousands',path: ['Life', 'Financial', 'Income'],},]},MonthlySpent: [{id: 105,name: 'Just a little bit',path: ['Life', 'MonthlySpent'],},{id: 106,name: 'Living Large',path: ['Life', 'MonthlySpent'],},]}};
    
    console.log(translate(myCurrentObject));

    A bit more compressed into a single expression, you could write it as:

    const translate = obj => Array.isArray(obj) ? obj
        : Object.keys(obj).map(key => 
             ({ name: key, obj_children: translate(obj[key]) })
        );
    
    let myCurrentObject = {Movies: {Comedy: {TopRanked: [{id: 99,name: 'The Interview',path: ['Movies', 'Comedy', 'TopRanked'],},{id: 100,name: 'Eurotrip',path: ['Movies', 'Comedy', 'TopRanked'],},]},Action: [{id: 101,name: 'We Were Soldiers',path: ['Movies', 'Action'],},{id: 102,name: 'The Bell',path: ['Movies', 'Action'],},]},Life: {Financial: {Income: [{id: 103,name: 'Making Hundreds',path: ['Life', 'Financial', 'Income'],},{id: 104,name: 'Making Thousands',path: ['Life', 'Financial', 'Income'],},]},MonthlySpent: [{id: 105,name: 'Just a little bit',path: ['Life', 'MonthlySpent'],},{id: 106,name: 'Living Large',path: ['Life', 'MonthlySpent'],},]}};
    
    console.log(translate(myCurrentObject));
    Reply

Leave a Comment