Search code examples
javascriptdata-structuresappendjavascript-objects

How to dynamically append key/values to a nested object


I am trying to append to an object/dictionary of sorts that adds categories and subcategories as they are found. However, each time I try to add them it seems like I just get the last element added to the nested object and overwrites the previous additions. I've tried using Object.assign() and {...obj, key:value} to no avail.

I have multiple for-loops that independently add each key:value pair after the database has been queried.

I am looking to create an object resembling this:

{
  category1: {
    subcategory1: null,
    subcategory2: null,
    subcategory3: {
      microcategory1: null
    }
  },
  category2: {
    subcategory1: null,
    subcategory2: null,
    subcategory3: null
  }
}

But am getting this instead:

{
  cooking: {
    'ranges': null
  },
  sinks: {
    'filters': null
  }
}

There should be 10+ subcategories for both cooking and sinks.

This is the code as it stands:

let categoriesDictObj = {};

for (var index = 0; index < categories.length; index++) {
  let category = categories[index];

  const subCategories = await Product.find({
    productCategory: category,
  }).distinct("productSubCategory");

  for (
    var subCatIndex = 0; subCatIndex < subCategories.length; subCatIndex++
  ) {
    let subCategory = subCategories[subCatIndex];

    const microCategories = await Product.find({
      productMicroCategory: subCategory,
    }).distinct("productMicroCategory");


    // categoriesDictObj[category] = { [subCategory]: null };

    // Object.assign(categoriesDictObj, {
    //   [category]: { [subCategory]: null },
    // });

    categoriesDictObj = {
      ...categoriesDictObj,
      [category]: {
        [subCategory]: subCatIndex
      },
    };
  }
}

Solution

  • You're missing to spread the previous categoriesDictObj[category] in the inner object, otherwise you're always overwriting the category object with a completely new one and only the last (with a single subCategory key) will prevail.

    categoriesDictObj = {
      ...categoriesDictObj,
      [category]: {
        ...categoriesDictObj[category],
        [subCategory]: subCatIndex
      },
    };
    

    However, there's no need to make it that complicated. Just create the inner object, fill it in the inner loop, and then assign it as part of the categoriesDictObj in the outer loop:

    const categories = …;
    const categoriesDictObj = {};
    
    for (let index = 0; index < categories.length; index++) {
      const category = categories[index];
      const subCategories = …;
    
      const subCategoriesDictObj = {};
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      for (let subCatIndex = 0; subCatIndex < subCategories.length; subCatIndex++) {
        const subCategory = subCategories[subCatIndex];
        const microCategories = …;
    
        subCategoriesDictObj[subCategory] = subCatIndex;
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      }
      categoriesDictObj[category] = subCategoriesDictObj;
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    }
    

    You could also make the variables mutable and keep the objects immutable by replacing the subCategoriesDictObj[subCategory] assignment with

    subCategoriesDictObj = {
      ...subCategoriesDictObj,
      [subCategory]: subCatIndex
    };
    

    and the categoriesDictObj[category] asssignment with

    categoriesDictObj = {
      ...categoriesDictObj,
      [category]: subCategoriesDictObj
    };
    

    but that's really just pointless inefficiency.