Smart use of references to convert arrays to tree arrays

Keywords: Javascript JSON Attribute

Preface

A project by the author needs to be a front-end tree menu. The data returned by the back-end is a parallel list. Each element in the list is an object, such as the value of list[0] is {id: 1, fid: 0, name: first-level menu}. Each element specifies a parent element, and the resulting menu can be nested indefinitely.The plug-in you were looking for at first needed to manually pass in the generated tree array to use (although you later found a UI framework that could pass directly into the list, just specify the ID and fid), but it wasn't written out correctly for a long time, so when you were free, you carefully thought about it and organized it into notes for later review.

Dead work

Because it is front-end processing, the implementation language in this paper is js.

Here is a parallel list list list and a root node not in the list:

var s = [
  { id: 1, fid: 0, name: "Level 1 Menu 1" },
  { id: 2, fid: 0, name: "Level 1 Menu 2" },
  { id: 3, fid: 1, name: "Level 2 Menu 1.1" },
  { id: 4, fid: 1, name: "Level 2 Menu 1.2" },
  { id: 5, fid: 2, name: "Level 2 Menu.1" },
  { id: 6, fid: 3, name: "Level 3 Menu 1.1.1" },
  { id: 7, fid: 3, name: "Level 3 Menu 1.1.2" },
  { id: 8, fid: 4, name: "Level 3 Menu 1.2.1" },
  { id: 9, fid: 4, name: "Level 3 Menu 1.2.2" },
  { id: 10, fid: 6, name: "Level 4 Menu 1.1.1.1" },
  { id: 11, fid: 6, name: "Level 4 Menu 1.1.1.2" },
  { id: 12, fid: 9, name: "Level 4 Menu 1.2.2.1" },
  { id: 13, fid: 9, name: "Level 4 Menu 1.2.2.2" },
  { id: 14, fid: 0, name: "Level 1 Menu 3" }
]

var root = { id: 0, fid: 0, name: "Root Menu" };

You need to sort it out like this, and if the node has no children, there is no node property:

{
    id: xx,
    fid: xx,
    name: xx,
    node: [
        id: xx,
        fid: xx,
        name: xx,
        node: [...]
    ]
}

A shuffle algorithm that disrupts the list order is needed, which affects the original array:

function shuffle(a) {
  var len = a.length;
  for (var i = 0; i < len; i++) {
    var end = len - 1;
    var index = (Math.random() * (end + 1)) >> 0;
    var t = a[end];
    a[end] = a[index];
    a[index] = t;
  }
};

Use JSON serialization to implement deep copies of arrays:

function deepCopy(arr) {
  return JSON.parse(JSON.stringify(arr));
}

Use a simple way to preliminarily determine if the results are correct:

function check(node) {
    return JSON.stringify(node).match(/menu/g).length;
}

Use recursion

[Ideas]

Recursion is a convenient way to solve this problem because you don't know exactly how many layers to loop through.

[Step]

1. Walk through the current list to find the node whose fid is the id of the incoming parent element and hang it on the node of the parent element;

2. Delete this element from the current list whenever a node is found (otherwise how recursively terminates);

3. For each child node, repeat the steps above and continue to find the child node of the node as the parent of the next layer.

As you can see, the worst time complexity is O(n!).

[Realization]

function arr2tree(arr, father) {
  // Traverse the array to find all the child nodes of the current father
  for (var i = 0; i < arr.length; i++) {   
    if (arr[i].fid == father.id) {
      // This is where the node property is required only if there are children (that is, there is never an empty list in a node)
      if (!father.node) { 
        father.node = [];
      }
      var son = arr[i];
      father.node.push(son);
      arr.splice(i, 1); // Delete the node and terminate recursion when list is empty
      i--; // Since the i node was deleted, the element subscript for the next visit should still be i
    }
  }
  // Do the same for each child node
  if (father.node) { // You need to first determine if there are any child nodes
    var childs = father.node;
    for (var i=0; i<childs.length; i++) {
      arr2tree(arr, childs[i]); // Calling Recursive Functions
    }
    // Used to sort by name, but can be removed if order is not emphasized
    father.node.sort(function (a, b) {
      return a.name > b.name;
    })
  }
}

[Inspection]

shuffle(s); // Shuffle array
var arr = deepCopy(s); // Copy one copy to avoid modifying the original array
arr2tree(arr, root);
console.log(check(root)); // Expected Output 15
console.log(root); // Check manually if the output is correct

Do not use recursion

[Ideas]

When there is a large amount of data, use recursion and it is easy to run out of memory. Is there no way to use recursion?Can we do it directly in a loop?If you can traverse this element, you can put it directly in the right place, which will save a lot of things.You can store these elements in a hash table (dictionary/object), where the key (attribute name) is the id of the element, so that you can directly tell that the parent element of the element you are traversing is not in the hash table.

Suddenly, the author thought of a feature - reference, the objects in js are all references, even if I have push a object into a list, any modifications I make to a object later will be reflected in the list.That is, I hang element a onto the corresponding parent element f, and when I find the child element b of element a later, I hang that child element b onto a, so does the mounted a in F.

[Step]

1. Create a new object temp to hold temporary information.Traverse the list and add the current accessing element a to temp (attribute name is object id, attribute value is that object);

2. Find out if there are children of a in temp, and if there are children, hang them on a.

3. Find if there is a parent node in temp, or if there is one, hang a onto the parent node;

As you can see, the time complexity is O(n^2^), the space complexity is not too high, and the method does not modify the original array.

[Realization]

function arr2tree2(arr, root) {
  var temp = {};
  temp[root.id] = root;
  for (var i = 0; i < arr.length; i++) {
    // Insert a new node whose subsequent modifications will be synchronized to its parent node
    temp[arr[i].id] = arr[i];
    // Find if there are child nodes
    var keys = Object.keys(temp);
    for (var j = 0; j < keys.length; j++) {
      if (temp[keys[j]].fid == arr[i].id) {
        temp[arr[i].id].node ? "" : temp[arr[i].id].node = [];
        temp[arr[i].id].node.push(temp[keys[j]]); // Hang this child node on the node of the current node
      }
    }
    // Find if there is a parent node
    if (temp[arr[i].fid]) {
      temp[arr[i].fid].node ? "" : temp[arr[i].fid].node = [];
      temp[arr[i].fid].node.push(arr[i]); // Hang the current node on the parent node's node
    }
  }
  return temp;
}

[Inspection]

shuffle(s); // Shuffle array
var result = arr2tree2(s, root);
console.log(check(result[root.id])); // Expected Output 15
console.log(result[root.id]); // Check manually if the output is correct

summary

Normally, most of the projects I do do do not involve algorithms, which can also be solved by loops. You can see that algorithms are still very important for programmers. This article is just an individual idea. Welcome to discuss together.

Posted by idweb on Sun, 17 Nov 2019 04:40:49 -0800