目录
前提
在JavaScript中遍历一个长度和深度都不确定的多层嵌套object,是一件难受的事情
关于object的多层遍历,网上搜到了许多答案,有递归、有循环然后用try catch排错
object子属性调用,一般用链式
举个例子
举个数据量大的例子,假设有一百科书,仅目录就有一本书那么厚。这套百科全书采用开放词条,目录会经常变化,层级和长度都不固定。
思路
代码
生成map与序列表的函数,代码:
//生成map与序列表的函数
function objExpansionMap(obj) { //obj展平成非嵌套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); //单个值不用拆开
return map;
}
let root = Object.entries(obj); //根
let ind = [0]; //子在何层(深度),第几个(序列)-序列数组
let tmpInd, tmpChild;
let parent = root.concat(); //父
let child, lens, tmpParent; //子、子键值对中值的长度、暂存父
let indMap = new Map(); //序列表
for (let i = 0; i < parent.length; i++) {
parent != tmpParent && (ind[ind.length - 1] = i); //判断父更新
tmpInd = ind.concat(); //临时ind
child = getItem(root, ind); //找子
lens = 0; tmpChild = null;
if (child[1] !== null && child[1] !== undefined) { //非空
lens = Object.keys(child[1]).length;
}
if (typeof child[1] == "object") {
if (lens > 0) { //子长不为零
ind.push(0);
parent = Object.entries(child[1]); //obj处理成键值对
i = -1;
} else if (i < parent.length - 1) { //子长为0,非末尾
let broInd = ind.concat();
broInd[broInd.length - 1] += 1;
//无弟,跳过,因为空集也会占一个序列,不跳过会出错
if (getItem(root, broInd) == undefined) { continue; }
}
} else {
tmpChild = child[1]; //非obj,写入具体值
}
if (i == parent.length - 1) { //末尾
tmpParent = undefined;
//找父的弟
while (tmpParent == undefined && ind.length > 1) {
ind.pop();
ind[ind.length - 1] += 1;
tmpParent = getItem(root, ind);
}
//结束判断
if (tmpParent != undefined || ind.length > 1) {
parent = tmpParent; //更新父
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); //写入map
}
return {
map: map,
indMap: indMap
}; //返回map
}
function getItem(item, ind) { //根据序列数组ind找子
for (let j = 0; j < ind.length; j++) {
item = item[ind[j]]; //找子
if (j == ind.length - 1) { break; } //末尾
item instanceof Array ? //将数组默认为键值对,并判断
item = Object.entries(item[1]) : //子为数组,返回子之值的键值对
item = Object.entries(item); //子非数组,返回自身键值对
}
return item; //返回子
}
调用生成map的代码(页面中只需调用一次,除非数据更新),代码:
//map,indMap用来接收函数输出的数据
const {map,indMap} = globalFnWP.objExpansionMap(menus);
生成map以后,在JavaScript中调用map里的属性,代码:
console.log(map.get("0")); //根据“索引”调用
console.log(indMap.get("home")()); //根据“名称”调用,名称不能重复
以下是将要被生成map的object,根据实际需要替换,建议不要嵌套数组:
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')
}
}
以下是根据上图中的数据输出的后台数据截图: