Turn a one-dimensional array of arrays into a structure of nested objects

As a novice, I am trying to figure out how to convert an array of values into nested objects based on categories. If the category (i.e., 1st element) exists, then the subsequent subcategories should be nested within it. Can someone please assist me with achieving this goal?

For example:

    const newCat = [
                     [
                        "Grocery", // category
                        "Food & Drink", // sub-category
                        "Snacks, Crisps and Sweets", // sub-sub-category
                    ],
                    [
                        "Grocery",
                        "Canned, Dry & Packaged Food",
                        "Pickled Foods",
                    ],
                    [
                        "Grocery",
                        "Food & Drink",
                    ],
                    [
                        "Grocery",
                        "Food & Drink",
                        "Nuts, Dates & Dried Fruits",
                    ],
                    [
                        "Grocery",
                        "World Specialities",
                        "India",
                    ],

                  ]
OUTPUT -
[
    {
    CategoryName: "Grocery",
    SubCategories: [
      {
        CategoryName: "Food & Drink",
        SubCategories: [
          {
            CategoryName: "Snacks, Crisps, and Sweets",
          },
        ],
      },
      {
        CategoryName: "Canned, Dry & Packaged Food",
        SubCategories: [
          {
            CategoryName: "Pickled Foods",
          },

        ],

      },
    ],
  }
]

Answer №1

function flattenNestedArray(arr) {
  const result = [];
  for (let item of arr) {
    const [c1, c2, c3] = item;
    const findC1 = result.find(c => c.categoryName === c1);
    if (findC1) {
      if (!c2) {
        continue;
      }
      const findC2 = findC1.subCategorys.find(c => c.categoryName === c2);
      if (findC2) {
        if (!c3) {
          continue;
        }
        findC2.subCategorys.push(c3);
      } else {
        findC1.subCategorys.push({
          categoryName: c2,
          subCategorys: [c3]
        });
      }
    } else {
      result.push({
        categoryName: c1,
        subCategorys: [
          {
            categoryName: c2,
            subCategorys: [c3]
          }
        ]
      })
    }
  }  
  
  return result;
}


// Test case
const nestedCategories = [
   [
      "Grocery", // category
      "Food & Drink", // sub-category
      "Snacks, Crisps and Sweets", // sub-sub-category
  ],
  [
      "Grocery",
      "Canned, Dry & Packaged Food",
      "Pickled Foods",
  ],
  [
      "Grocery",
      "Food & Drink",
  ],
  [
      "Grocery",
      "Food & Drink",
      "Nuts, Dates & Dried Fruits",
  ],
  [
      "Grocery",
      "World Specialities",
      "India",
  ],

];

console.log(flattenNestedArray(nestedCategories));

Answer №2

const categories = [
  [
    "Grocery", // Main category
    "Food & Drink", // Sub-category
    "Snacks, Crisps and Sweets", // Sub-sub-category
  ],
  [
    "Grocery",
    "Canned, Dry & Packaged Food",
    "Pickled Foods",
  ],
  [
    "Grocery",
    "Food & Drink",
  ],
  [
    "Grocery",
    "Food & Drink",
    "Nuts, Dates & Dried Fruits",
  ],
  [
    "Grocery",
    "World Specialities",
    "India",
  ],
  ["Electronics", "Speakers", "Bluetooth Speakers", "JBL", ],
]

const outputCategories = []

categories.forEach((e) => {
  let indexOfMainCategory = outputCategories.findIndex((category) => category.CategoryName === e[0]);
  if (indexOfMainCategory === -1) {
    outputCategories.push({
      CategoryName: e[0],
      SubCategories: []
    })
    indexOfMainCategory = outputCategories.length - 1;
  }
  if (e[1]) {
    let indexOfSubcategory = outputCategories[indexOfMainCategory].SubCategories.findIndex((category) => category.CategoryName === e[1]);
    if (indexOfSubcategory === -1) {
      outputCategories[indexOfMainCategory].SubCategories.push({
        CategoryName: e[1],
        SubCategories: []
      })
      indexOfSubcategory = outputCategories[indexOfMainCategory].SubCategories.length - 1;
    }
    if (e[2]) {
      let indexOfSubSubcategory = outputCategories[indexOfMainCategory].SubCategories[indexOfSubcategory].SubCategories.findIndex((category) => category.CategoryName === e[2]);
      if (indexOfSubSubcategory === -1) {
        outputCategories[indexOfMainCategory].SubCategories[indexOfSubcategory].SubCategories.push({
          CategoryName: e[2],
          SubCategories: [],
        })
        indexOfSubSubcategory = outputCategories[indexOfMainCategory].SubCategories[indexOfSubcategory].SubCategories.length - 1;
      }
      if (e[3]) {
        const indexOfSubSubSubcategory = outputCategories[indexOfMainCategory].SubCategories[indexOfSubcategory].SubCategories[indexOfSubSubcategory].SubCategories.findIndex((category) => category.CategoryName === e[3]);
        if (indexOfSubSubSubcategory === -1) outputCategories[indexOfMainCategory].SubCategories[indexOfSubcategory].SubCategories[indexOfSubSubcategory].SubCategories.push({
          CategoryName: e[3]
        })
      }
    }
  }
});

console.log(outputCategories)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Scrolling to top using jQuery - Problem with incorrect anchor

I am attempting to implement a scrollTop animation that scrolls to an anchor within a fullscreen <section>. However, the issue is that it does not scroll to the correct anchor on the first click. Below is the code snippet. <nav id="scroller"> ...

Prevent regex from matching leading and trailing white spaces when validating email addresses with JavaScript

In my current setup, I utilize the following regular expression for email validation: /^[a-zA-Z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/ My attempt to validate the email is shown below: if (!event.target.value.match(/^[a-zA-Z0-9._%+-]+@[a-z0-9.-]+\. ...

Prevent textArea from reducing empty spaces

I am facing an issue with my TextEdit application set to Plain Text mode. When I copy and paste text from TextEdit into a textarea within an HTML form, the multiple spaces get shrunk. How can I prevent the textarea from altering the spacing in the text? T ...

In Rails 3, you can utilize JavaScript to submit a form_tag remotely, however ensure that it submits as

I am trying to implement a form_tag in rails 3 that can be submitted using ajax instead of html through javascript. Despite setting the form to submit as javascript, it still submits as html when clicking the submit button. - form_tag({:controller => " ...

The ajax method is encountering an issue when trying to perform an action: It is unable to find the necessary anti-forgery form field "__RequestVerificationToken" required for the operation

I am encountering an issue with my ajax method that triggers the action method from the controller. When I run this method, I receive an error stating: The required anti-forgery form field "__RequestVerificationToken" is not present. However, upon inspecti ...

Issue encountered when calling theme.breakpoints.down('') function from Material UI

As a novice, I have ventured into using material UI on the front-end of my project. My aim is to achieve responsiveness by leveraging theme.breakpoints.down as indicated in the material UI documentation. However, when attempting to implement this, I encoun ...

"An error has occurred: React's this.setState function cannot

This draft of the Typehead class is still a work in progress, with the final version intended to display a list of choices from props that match the user input, essentially functioning as an autocomplete feature. I am encountering an error message "cannot ...

What is the best way to add a button to every row in Titanium Studio?

Is there a way to add a different button inside each row (createTableViewRow)? I have created five buttons using Titanium.UI.createButton, but I'm struggling to figure out how to place all five buttons in every row. Can someone provide some guidance o ...

What is the best way to continuously compare two date variables every minute using Javascript?

In my script, I have two date variables - one representing the current time and the other two minutes later. My goal is to compare both values every minute and trigger a function when the current time is greater than or equal to the latter time. Unfortun ...

Does Javascript support annotation? If not, what is the best way to easily switch between debug and productive modes in a declarative manner?

I have a question that has been on my mind. I have searched Google but couldn't find any helpful links, so I thought it would be best to seek advice from experts here. My main concern is whether there is a way to create annotations in JavaScript sour ...

What is the best way to transfer the search query to a table filter when working with multiple JavaScript files?

I am struggling with passing the search query from my search file to my table file. The data for my datagrid table is retrieved from a database using an API call, and the table code is in one file while the search functionality code is in another file. I h ...

Convert the jade file to an HTML file while keeping the original file name

I'm currently attempting to configure Jade in a way that allows me to save my Jade files as HTML files while retaining the same file name. For example, I would like the file views/index.jade to be saved as dist/index.html This should apply to all ad ...

What is the best way to access the authData while deleting a user from Firebase?

Previously, when deleting data from Firebase, I used the following code: MyFirebaseRef.on('child_removed', function (oldChildSnapshot) { /* oldChildSnapshot => the data that's been erased */ }); Now, however, I want to achieve the ...

Invoking a parent controller method from a directive in AngularJS

I have utilized a tree grid plugin from the following link: https://github.com/khan4019/tree-grid-directive and I made some customizations to its template: .directive('treeGrid', [ '$timeout', function($timeout) { return { ...

Navigating URLs to Single Page Application Routing in Vue.js

I'm new to using vue-router and I'm curious if there's a way to manage redirection in Vue or if there are alternative methods in a node.js environment. For instance, when someone tries to access my site by typing the URL example.com/contac ...

Issue with ThreeJS AdditiveBlending, ShaderMaterial, and DepthTest

As I work on creating a scene with a variety of objects, I drew inspiration from a CodePen example by gnauhca (https://codepen.io/gnauhca/pen/VzJXGG). In the example, DepthTest is disabled on the ShaderMaterial, but I actually need it to be enabled in orde ...

Insert a picture within the text input field

I'm facing a specific scenario where I need to add text followed by an image, then more text followed by another image and so on. Please see the input text with values in the image below. Can someone guide me on how to accomplish this with jQuery and ...

Encountering an issue while implementing a fresh design using Material-UI Version 5 and React Version 18.01 - Specifically facing problems with the '@mui/styles' package

Hi there! I am currently working with react V.18.01 and Material-ui v.5 for my application development, but encountered an error that I need assistance with. Being a beginner developer, I would greatly appreciate a code review to help me understand the iss ...

Placing elements in Chrome compared to IE

I'm currently attempting to position elements in two rows using mathematical calculations. One of the elements, thumb_container, is a div that is absolutely positioned. Within this container, I am dynamically loading and appending image thumbnails usi ...

The click function may need an additional click to complete its execution, but ideally, it should be accomplished in just one click

I have a script that filters elements on a webpage by checking the data attribute of the clicked element against class names on the filtered items (.filter-boy). As the elements are categorized into Categories and Subcategories, I aim to hide any parent c ...