简体   繁体   中英

Convert Array of Objects to Nested Object in Javascript

I would like to convert an array of objects to a nested object using a recursive function.

The objective is to create a function that works whatever the depth of my inital array. You can see below the initial data with desired result + code snippet with attempt at solving this issue.

Initial array of objects

configurator: [
  {
    key: '-LLnLuLt6cn-vBpMWv-u',
    name: 'CONFIGURATOR_1',
    collections: [
      {
        key: '-LLnMWy69vACjys0QIGH',
        name: 'COLLECTION_1',
        options: [
          {
            key: '-LLnOxg5hsDYR-PcfjBT',
            name: 'OPTION_1',
          },
          {
            key: '-LLnP-O6TyHxIpPk9bCU',
            name: 'OPTION_2',
          },
        ],
      },
      {
        key: '-LLnMYNyJmhSCPB-8lL1',
        name: 'COLLECTION_2',
      },
    ],
  },
  { key: '-LLnLtLs7PjXSAW0PWCQ',
    name: 'CONFIGURATOR_2',
  }]

Desired outcome : Nested objects

configurator: {
  '-LLnLuLt6cn-vBpMWv-u': {
    name: 'CONFIGURATOR_1',
    index: 0,
    collections: {
      '-LLnMWy69vACjys0QIGH': {
        name: 'COLLECTION_1',
        index: 0,
        options: {
          '-LLnOxg5hsDYR-PcfjBT': {
            name: 'OPTION_1',
            index: 0,
          },
          '-LLnP-O6TyHxIpPk9bCU': {
            name: 'OPTION_2',
            index: 1,
          },
        },
      },
      '-LLnMYNyJmhSCPB-8lL1': {
        name: 'COLLECTION_2',
        index: 1,
      },
    },
  },
  '-LLnLtLs7PjXSAW0PWCQ': {
    name: 'CONFIGURATOR_2',
    index: 1,
  },
}

My attempt

Here is a code snippet of what I have tried so far. It only works with the first depth of the array. I believe this is the challenge to solve: how to dynamcilly add/'push' an object to a nested object ?

Hope someone can help. Cheers, Julien.

 const data = { configurator: [{ key: '-LLnLuLt6cn-vBpMWv-u', name: 'CONFIGURATOR_1', collections: [{ key: '-LLnMWy69vACjys0QIGH', name: 'COLLECTION_1', options: [{ key: '-LLnOxg5hsDYR-PcfjBT', name: 'OPTION_1', }, { key: '-LLnP-O6TyHxIpPk9bCU', name: 'OPTION_2', }, ], }, { key: '-LLnMYNyJmhSCPB-8lL1', name: 'COLLECTION_2', }, ], }, { key: '-LLnLtLs7PjXSAW0PWCQ', name: 'CONFIGURATOR_2', } ] }; const format = (object) => { const result = {}; Object.keys(object).forEach((property) => { if (Array.isArray(object[property])) { object[property].forEach((test, index) => { const { key, ...content } = test; result[key] = { index, ...content }; format(content); }); } }); return result; }; const formated = format(data); console.log('@FORMATED__', formated); 

Indeed this has to be done recursively. Just reduce the arrays into an object. For each object, find the keys that have arrays as values, then apply the same to them:

const format = array => 
    array.reduce((acc, obj, index) => {
        const {key, ...rest} = obj;
        Object.keys(rest)                                            // get the keys of the rest of the object
              .filter(key => Array.isArray(rest[key]))               // filter those that have arrays as values
              .forEach(key => rest[key] = format(rest[key]));        // for each one of them, format their array and assign them back to the rest object
        acc[key] = { ...rest, index };                               // create the new object and assign it to the accumulator
        return acc;
    }, {});

Example:

 const format = array => array.reduce((acc, obj, index) => { const {key, ...rest} = obj; Object.keys(rest) .filter(key => Array.isArray(rest[key])) .forEach(key => rest[key] = format(rest[key])); acc[key] = { ...rest, index }; return acc; }, {}); const arr = [{"key":"-LLnLuLt6cn-vBpMWv-u","name":"CONFIGURATOR_1","collections":[{"key":"-LLnMWy69vACjys0QIGH","name":"COLLECTION_1","options":[{"key":"-LLnOxg5hsDYR-PcfjBT","name":"OPTION_1"},{"key":"-LLnP-O6TyHxIpPk9bCU","name":"OPTION_2"}]},{"key":"-LLnMYNyJmhSCPB-8lL1","name":"COLLECTION_2"}]},{"key":"-LLnLtLs7PjXSAW0PWCQ","name":"CONFIGURATOR_2"}]; console.log(format(arr)); 

Note: If, for example, there could be some arrays that don't contain only objects that you want to skip, then simply change the filtering from:

.filter(key => Array.isArray(rest[key]))

To:

.filter(key => Array.isArray(rest[key]) && rest[key].every(item => item && typeof item === "object"))

Which checks if the array contains only objects before apply format on it.

Edited

function format(data) {
  var result = {};

  data.forEach(function(config, i) {
    var obj = {
        index: i
    }

    Object.keys(config)
        .forEach(function(key) {
            if (!Array.isArray(config[key])) {
                if (key != "key") obj[key] = config[key];
            } else {
                obj[key] = format(config[key]);
            }
        });

    result[config.key] = obj;
  });

  return result;
}
console.log(format(data));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM