Transform a one-dimensional array arranged in ASCII order into a hierarchical array structure using Javascript

Consider the array of objects below, sorted by the code property in ascending ASCII order:

var codes = [
    { code: '01' },
    { code: '0101' },
    { code: '0102' },
    { code: '010201' },
    { code: '0103' },
    { code: '02' },
    { code: '0201' },
    { code: '0202' },
];

Is there a way to transform this into a nested array structure like so :

var nestedCodes = [
    {
        code: '01',
        children: [
            { code: '0101' },
            {
                code: '0102',
                children: [
                    { code: '010201' }
                ]
            },
            { code: '0103' }
        ]
    },
    {
        code: '02',
        children: [
            { code: '0201' },
            { code: '0202' }
        ]
    }
];

The organization of codes involves combining multiple 0N, where N could range from 1 to 9. It's worth noting that the codes are retrieved from a server and may have additional properties apart from code, such as title, which is irrelevant to this issue.

The primary objective here is to create a suitable format for use with jsTree.

Answer №1

To achieve this task, you can utilize a recursive approach. The strategy involves keeping track of the path (obtained as an array through String.prototype.match using a regex) and the parent where you intend to insert the code for each iteration of the recursive function.

The parent variable denotes the specific node to select in the current recursive call, while the path aids in constructing the hierarchy by delving deeper into the nested elements:

function insert(d, path, parent, arr) {
  if (path.length === 0) {
    arr.push(Object.assign({}, d));
    return;
  }
  var target = arr.find(e => e.code === parent);
  target.children = target.children || [];
  insert(d, path.slice(1), parent + path[0], target.children);
}

var codes = [
    { code: '01' },
    { code: '0101' },
    { code: '0102' },
    { code: '010201' },
    { code: '0103' },
    { code: '02' },
    { code: '0201' },
    { code: '0202' },
];

var res = codes.reduce((a, c) => {
  var p = c.code.match(/(0[1-9])/g);
  insert(c, p.slice(1), p[0], a);
  return a;
}, []);

console.log(res);

It is crucial to note that the implementation assumes that the parent of a particular code has already been inserted earlier in the process.

Answer №2

It was a challenge for me to create the recursive function needed to construct the desired structure. I found the solution here

In order to achieve this, it's necessary to include a parent property in each element of the codes array. My approach involved assuming that each code has a parent which is essentially the same code, except for the last two characters.

var codes = [{code: '01'    },
             {code: '0101'  },
             {code: '0102'  },
             {code: '010201'},
             {code: '0103'  },
             {code: '02'    },
             {code: '0201'  },
             {code: '0202'  },
          ];

// adding parents to each code
codes.forEach(function(c) {
  if (c.code.length > 2) {
    c.parent = c.code.substr(0, c.code.length - 2);
  } else {
    c.parent = 'root';
  }
});



function find_children(arr, parent) {
  var output = [];
  for (var i in arr) {
    
    if (arr[i].parent == parent) {
      
      var children = find_children(arr, arr[i].code);

      if (children.length) {
        arr[i].children = children;
      }
      output.push(arr[i])
    }
  }
  return output;
}

var nested = find_children(codes,'root');
console.log(nested);

Answer №3

The code provided may seem a bit lengthy at first glance, but it is actually quite easy to comprehend. It boasts high robustness by not requiring the array to be sorted and can process strings like "0102" without needing "01" to exist beforehand. While the code could be shorter if these cases were not handled, I included them for your convenience.

To begin, an object-based tree data structure is generated from the input data. This tree is efficient due to its O(1) index access. Subsequently, the object-based tree is converted into the final array-based tree by traversing each layer of the object-based tree and transforming them into arrays.

Recursion plays a significant role in this implementation as it proves to be ideal for both creating and navigating through trees efficiently.

In comparison to other solutions, my algorithm exhibits superior time complexity thanks to the creation of a dictionary/object that allows for O(1) access during tree generation. On the contrary, alternative algorithms perform searches within each layer, resulting in inefficiency. My approach operates in O(N) time complexity, whereas others found here are shorter yet run in O(N^2).

You can simply copy the format function into your project and utilize it seamlessly.

const codes = [
    { code: '01' },
    { code: '0101' },
    { code: '0102' },
    { code: '010201' },
    { code: '0103' },
    { code: '02' },
    { code: '0201' },
    { code: '0202' },
];

function format(codes) {
  // Splits the string into an array of 2-character strings.
  const SPLIT_REGEX = /.{2}(?=(.{2})+(?!.))|.{2}$/g;
  const codeFragments = codes.map(obj => obj.code.match(SPLIT_REGEX));

  // 1. Represent the data as a tree which is more efficient to build.
  const tree = {};
  function createTree(tree, fragments) {
    let node = tree;
    fragments.forEach(fragment => {
      if (!node[fragment]) {
        node[fragment] = {};
      }
      node = node[fragment];
    });
  }
  codeFragments.forEach(fragments => createTree(tree, fragments));
  
  // 2. Convert the tree structure into the desired format.
  function generateCodesFromTree(tree, previous) {
    const nestedCodes = [];
    Object.keys(tree).forEach(treeNode => {
      const code = previous + treeNode;
      const children = generateCodesFromTree(tree[treeNode], code);
      const nestedCode = { code };
      if (children.length > 0) {
        nestedCode.children = children;
      }
      nestedCodes.push(nestedCode);
    });
    return nestedCodes;
  }

  return generateCodesFromTree(tree, '');
}

console.log(format(codes));

Answer №4

To solve this problem, one must take a recursive approach. Give the following solution a try:

let codes = [
    { code: '01' },
    { code: '0101' },
    { code: '0102' },
    { code: '010201' },
    { code: '0103' },
    { code: '02' },
    { code: '0201' },
    { code: '0202' },
];

roots = codes.filter(c => c.code.length === 2);

roots.forEach(c => assign(c));

console.log(roots);

function assign(code) {
  codes.forEach(c => {
    if (c !== code) {
      if (code.code === c.code.slice(0, code.code.length)) {
        code.children = !code.children ? [c] : [...code.children, c];
        assign(code.children[code.children.length - 1]);
      }
    }
  });
}

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

Utilizing Selenium and Python to extract data from JavaScript tables via web scraping

I've come across numerous articles about Web Scraping, but I'm still struggling to figure out how to locate specific elements on a website. The site where I want to scrape the tables can be found here: My target tables are: "TB01," "TB02," "TB0 ...

What is the best way to place a div within another div?

I am facing a challenge where I need to insert a new div within another div before all its existing contents. The issue is that the outer divs do not have unique IDs, only classes as selectors. Unfortunately, I do not have control over the generation of th ...

Ways to append multiple values to an object ID in mongoDB at a later time

I have a pre-existing object ID in my MongoDB database, and I am looking to add more values inside it in the future. Here is an example of my current MongoDB structure: [{ label: 'colors', options: [ { label: 'Bl ...

What is the best way to keep an image centered in the nav-bar as it decreases in size?

As I work on building a website using a template (https://github.com/learning-zone/web-templates/tree/master/victory-school-free-html5-bootstrap-template), I encountered an issue with the navigation bar. The original design appears like this: Before shrin ...

Unexpected outcome when utilizing localeCompare options in Node.js

Comparing Output from CLI and Chrome Console: > var a = 'foo12'; undefined > var b = 'foo3'; undefined > var s = [a , b]; undefined > s.sort(function(a, b) { ... return a.localeCompare(b, 'en', { numeric: true }); ...

Integrate Tailwind CSS into the bundled JavaScript file

Is there a way to integrate tailwind css into bundle js effectively? Take a look at this example involving vue 3 and tailwind 3 https://github.com/musashiM82/vue-webpack. Upon executing npm run build , it generates 3 files: app.js ABOUTPAGE.js app.6cba1 ...

Discover the simple steps to generating dynamic variables with jQuery!

I am looking to dynamically create jQuery variables based on values in a loop. Here is an example of what I am trying to achieve. array=["student","parent","employee"] $.each(user_types, function( index, value ){ var dynamicType = value + "_type"; // t ...

Summing values in es6 (TypeScript) without explicitly knowing their corresponding keys

I am facing a challenge with an object that has changeable keys, which I do not want to rely on. The keys in this object are not fixed. Here is an example: interface Inf { [key: string]: number } const obj: Inf = { '2020-01-01': 4, '2 ...

Handling dynamic routes with React Router 4 and the 404 path

I have been working with the latest version of React Router (4) and have implemented a dynamic route configuration as described in the tutorial. The routes are functioning correctly, except for when I tried to add a 404 path following the tutorial's i ...

Use Jquery to insert HTML content after every third iteration of a for loop

I am endeavoring to display a <div class="clear"></div> after every third iteration of a for loop in a jQuery context. In PHP, this can easily be achieved using if($i%3 == 0), but how does one go about implementing this in jQuery or JavaScript? ...

I'm struggling to activate the eventListener on several elements with the same className or ID. Unfortunately, only the initial child is being triggered in my current code implementation

Hello fellow developers, I'm facing an issue while working on a project. I have about ten menu items with the same ID, and I want to be able to edit each one when it is clicked. Here's what I tried using JavaScript: const menuElement = d ...

Retrieve a string value in Next.JS without using quotation marks

Using .send rather than .json solved the problem, thank you I have an API in next.js and I need a response without Quote Marks. Currently, the response in the browser includes "value", but I only want value. This is my current endpoint: export ...

Is there a way for me to generate a custom subtype of Error that can be thrown?

I am attempting to create my own custom error in order to handle it differently when catching it. I want to be able to call if(instanceof DatabaseError) and execute my specific handling logic. export class DatabaseError extends Error { constructor(...a ...

Determine all checkboxes in a table that are selected if the rows contain a display style tag using Javascript

Currently, I have a basic text filter that assigns a display property to rows in a table either as "none" or "table-row". My challenge lies in trying to modify my "select all" script so that it only interacts with the checkboxes that are visible on the pag ...

What is the best way to access data stored in the state of the store.js within a Vue application?

Currently, I am working on my initial project using Vue.js. The application involves a multi-step form that shares a common header and footer. As the user progresses through each step, the data entered is sent to store.js for storage. However, I have encou ...

Guide to enabling cross-domain uploads of files

After following a tutorial, I successfully implemented an HTML5 uploader on my website. The source of the tutorial can be found here: The uploader works perfectly; however, I am now facing an issue where I want to upload files to a different domain. Accor ...

Creating an array object in JavaScript using an integer variable involves utilizing the integer as the

I am trying to populate an array object with integer variables using the following code: for(i=1; i<=2; i++) arr[i].push({i:(100 * i)}) The expected result should be: arr = [{ 1:100,2:200},{1:100,2:200}] However, the issue is that the array is bein ...

Adjust the CSS for the "a" tag's "title" attribute

I am using jquery.fancybox to create an image modal on my website. I have noticed that I can add a description using the title attribute. However, when the description is too long, the text is displayed on a single line and some of it disappears. How can ...

Sorting nested JSON by property with NodeJS

If we consider a directory structure like the following: root |_ .git |_ .sass-cache |_ css | |_ scss | | |_ modules | | | |_ a-module.scss | | | |_ ... | | |_ partials | | | |_ a-partial.scss | | | |_ ... | | |_ main.scss | |_ main.cs ...

Managing Time Before Browser Refresh in AngularLearn how to monitor and examine the time remaining before

In my Angular web application, I am facing an issue where the user's login status is lost every time the browser is refreshed or reloaded. I want to implement a feature that allows the login status to remain active for a short period of time after the ...