current position:Home>JavaScript traverses the irregularly nested multi-layer objects, returns the map with "index", and realizes the rapid positioning of sub attributes

JavaScript traverses the irregularly nested multi-layer objects, returns the map with "index", and realizes the rapid positioning of sub attributes

2022-04-29 04:24:59An ant watcher

Catalog

Premise

for instance

Ideas

Code


Premise

         stay JavaScript Traverses a multi-layer nesting with uncertain length and depth object, It's a hard thing

  • object You can't use... Like an array foreach
  • for in、for of( Not recommended )、Object.key() and Object.entries()., A loop can only traverse single-layer objects , Want to traverse multiple nested objects , Much more complicated than expected

         About object Multilayer traversal of , Many answers have been found on the Internet , There is recursion 、 Have a loop and then use try catch misarrangement

  • Recursive logic is relatively simple , The main idea is to call itself inside the function , Not suitable for scenes with large amount of data .
  • The logic of the loop method is more complex , It is relatively safe for a large number of data
  • try catch Can not use , You don't have to

       object The sub attribute calls , Generally, chain type is used

  • Encounter a scene where objects are nested very complex , Chained calls are no longer elegant
  • In the same scope , There are many sub attribute calls with different nesting depths , Code can become difficult to maintain

for instance

         Take an example of a large amount of data , Suppose there is an encyclopedia , The catalogue alone is as thick as a Book . This encyclopedia uses open entries , The directory will change frequently , The level and length are not fixed .

  • Catalog data in object Multi layer nested structure storage
  • Attribute names have linguistic meaning , If you change to a pure array , Or it's hard to call , Or lose semantics .

Ideas

  • Preserve semantics 、 Hierarchy information and attribute values , have access to map Transfer data
  • To reduce the number of cycles , Simplify the hierarchy , The multi-layer nested structure needs to be “ Flattening ” Form a single-layer structure
  • To preserve hierarchical information 、 At the same time, avoid duplicate attribute names , Use the class array to write the information representing the depth and sequence number into the key name
  • Generated map It can be called repeatedly , Traversing a single-layer structure takes only one cycle
  • use map.get(“ Indexes ”) Check the attribute value , Avoid writing long chain calls

problem : Above map The key name of acts as “ Indexes ”, Is a comma separated string that can be converted into an array , It means that the key name is “ De semantic ” 了 , During the call , If you call the sub attribute, write it as map.get(“0,0,1,0”), Is not intuitive , How to solve ?

answer : Use map.get Get child properties , There is no need to write “ Indexes ”.

  • On the view level - Use flattened map One loop can write out the directory , But the directory will not show “0,0,1,0”, Instead, it displays a string with semantics , The statements executed by clicking on the directory are generated in batch , No need to input “ Indexes ”.( The specific implementation method of view layer will not be repeated )
  • In the code , Can generate “ Comparison table ”, Is another map object , The key name is a semantic string , The key value is a function, After calling, directly check the output in the above map Property value .
  • Because of the uniqueness of attribute names , stay “ Comparison table ” in , Equal attribute names will conflict .
  • Use Symbol Property names of types can avoid property conflicts , But at the same time, it will also make the key name lose semantic or hierarchical information , So it's not used Symbol.

Code

        Generate map Function with sequence table , Code :

// Generate map Function with sequence table 

function objExpansionMap(obj) {            //obj Flatten to non nested map
    let map = new Map();
    switch (true) {
        case obj === null: case obj === undefined:
            return obj;
        case /string|number|function/.test(typeof obj):
            map.set(0, obj);            // Individual values do not need to be disassembled 
            return map;
    }

    let root = Object.entries(obj);     // root 
    let ind = [0];                      // Where is the floor ( depth ), Number one ( Sequence )- Sequence arrays 
    let tmpInd, tmpChild;
    let parent = root.concat();         // Father 
    let child, lens, tmpParent;         // Son 、 The length of the value in the child key value pair 、 Temporary parent 
    let indMap = new Map();             // Sequence table 

    for (let i = 0; i < parent.length; i++) {
        parent != tmpParent && (ind[ind.length - 1] = i);   // Judge the parent update 
        tmpInd = ind.concat();                                // temporary ind
        child = getItem(root, ind);                         // Find a son 

        lens = 0; tmpChild = null;

        if (child[1] !== null && child[1] !== undefined) {    // Non empty 
            lens = Object.keys(child[1]).length;
        }

        if (typeof child[1] == "object") {
            if (lens > 0) {                         // The sub length is not zero 
                ind.push(0);
                parent = Object.entries(child[1]);  //obj Processing into key value pairs 
                i = -1;
            } else if (i < parent.length - 1) {     // The son's length is 0, Not at the end 
                let broInd = ind.concat();
                broInd[broInd.length - 1] += 1;
                // No brother , skip , Because an empty set will also occupy a sequence , If you don't skip, you will make an error 
                if (getItem(root, broInd) == undefined) { continue; }
            }
        } else {
            tmpChild = child[1];                      // Not obj, Write a specific value 
        }

        if (i == parent.length - 1) {               // At the end of 
            tmpParent = undefined;
            // Find your father's brother 
            while (tmpParent == undefined && ind.length > 1) {
                ind.pop();
                ind[ind.length - 1] += 1;
                tmpParent = getItem(root, ind);
            }
            // End judgment 
            if (tmpParent != undefined || ind.length > 1) {
                parent = tmpParent;                 // Update parent 
                i = -1;
            }
        }

        let value;
        if (tmpChild === null) {
            value = {
                name: child[0]
            }
        } else {
            value = {
                name: child[0],
                value: tmpChild
            }
        }
        let key = tmpInd.join(",");

        indMap.set(child[0], ()=>{
            return map.get(key);
        });

        map.set(key, value);                        // write in map

    }

    return {
        map: map,
        indMap: indMap
    };                                     // return map
}

function getItem(item, ind) {               // According to the sequence array ind Find a son 
    for (let j = 0; j < ind.length; j++) {
        item = item[ind[j]];                    // Find a son 
        if (j == ind.length - 1) { break; }     // At the end of 
        item instanceof Array ?                 // Default the array to key value pairs , And decide 
            item = Object.entries(item[1]) :    // Sub array , The key value pair that returns the value of the child 
            item = Object.entries(item);        // Subarray , Returns its own key value pair 
    }
    return item;                                // Return son 
}

        Call to generate map Code for ( You only need to call... Once in the page , Unless the data is updated ), Code :

//map,indMap Used to receive the data output by the function 
const {map,indMap} = globalFnWP.objExpansionMap(menus);

        Generate map in the future , stay JavaScript Call in map In the properties of the , Code :

console.log(map.get("0"));            // according to “ Indexes ” call 

console.log(indMap.get("home")());    // according to “ name ” call , The name cannot be repeated 

          The following is to be generated map Of object, Replace... According to actual needs , It is not recommended to nest arrays :

const menus = {
  home: () => import('@/views/home.vue'),
  effects: {
    jellyRun: () => import('@/views/effects/jellyRun.vue'),
    speadSmooth: () => import('@/views/effects/speadSmooth.vue')
  },
  games: {
    littleGames: {},
    bigGames: {
      Games3D: {
        ballGames: {},
        petGames: {}
      },
      RPG: {},
    }
  },
  tools: {
    counter: () => import('@/views/tools/counter.vue'),
    ryth: () => import('@/views/tools/ryth.vue')
  }
}

        The following is a screenshot of the background data output according to the data in the above figure :

 

copyright notice
author[An ant watcher],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/118/202204280551419570.html

Random recommended