如何在JavaScript中递归地构建菜单列表对象?
一系列如何在JavaScript中递归地构建菜单列表对象?,javascript,arrays,ecmascript-6,javascript-objects,arrow-functions,Javascript,Arrays,Ecmascript 6,Javascript Objects,Arrow Functions,一系列 ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium']; 我想构造一个映射对象,它看起来像: { 'social': { swipes: { women: null, men: null } }, 'upgrade': { premium: null } } const-menu
['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
我想构造一个映射对象,它看起来像:
{
'social': {
swipes: {
women: null,
men: null
}
},
'upgrade': {
premium: null
}
}
const-menu=['/social/swipes/women','/social/likes/men','/upgrade/premium'];
常量映射={};
常量addLabelToMap=(根,标签)=>{
如果(!map[root])map[root]={};
如果(!map[root][label])map[root][label]={};
}
const buildMenuMap=菜单=>{
菜单
//复制菜单
//.slice返回原始数组的副本
.slice()
//通过拆分/,将字符串转换为数组
//删除第一个,因为它是空的
//.map返回一个新数组
.map(item=>item.split('/').splice(1))
//遍历每个数组及其元素
.forEach((元素)=>{
设root=map[element[0]| |“”;
for(设i=1;i简单到:
root = root[label];
如果将助手函数更改为:
const addLabelToMap = (root, label) => {
if(!root[label]) root[label] = {};
}
我会这样写:
const buildMenuMap = menus => {
const root = {};
for(const menu of menus) {
const keys = menu.split("/").slice(1);
const prop = keys.pop();
const obj = keys.reduce((curr, key) => curr[key] || (curr[key] = {}), root);
obj[prop] = null;
}
return root;
}
使用reduce
而不是map
。在这种情况下,根将是累加器:
const buildMenuMap = menu =>
menu.reduce((root, item) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (acc[part] = {}), root);
leaf[lastPart] = null;
return root;
}, Object.create(null));
说明:
对于菜单
数组中的每个项
,我们首先去掉前面的“/”
(使用切片(1)
),然后通过拆分部分
然后,我们从这个结果数组中删除lastPart
(最后一部分与其余部分分开处理)
对于部分
数组中的每个剩余部分,我们遍历根
数组。在每个遍历级别,我们要么返回该级别的对象acc[part]
,如果它已经存在,,要么创建并返回一个新的对象,如果它不(acc[part]={})
在到达最后一级leaf
之后,我们使用lastPart
将值设置为null
请注意,我们将Object.create(null)
传递给reduce
Object.create(null)
创建一个无原型的对象,这样使用root[someKey]
就更安全了,而无需检查someKey
是否为自有属性
示例:
constbuildmenumap=menu=>
menu.reduce((根,项)=>{
让parts=item.slice(1).拆分(“/”);
让lastPart=parts.pop();
让leaf=parts.reduce((acc,part)=>acc[part]| |(acc[part]={}),root);
叶[lastPart]=null;
返回根;
},Object.create(null));
让arr=['/social/swipes/women','/social/swipes/men','/upgrade/premium'];
让结果=构建菜单映射(arr);
控制台日志(结果)代码>我刚刚调试了你的代码,看看哪里出了问题,我敦促你也这样做。你犯了两个(明显的)错误:
首先,在第一次迭代中,map
的值只是一个空对象{}
,root
的值被初始化为“
,标签
是滑动
.forEach((element) => {
let root = map[element[0]] || "";
...
root = root[label];
}
因此,您得到根[label]
是未定义的
,因此新根是未定义的
第二,您在任何地方都可以使用map
const addLabelToMap = (root, label) => {
if(!map[root]) map[root] = {};
if(!map[root][label]) map[root][label] = {};
}
相反,您应该将其作为一个参数,以便能够执行递归
const addLabelToMap = (root, label) => {
if(!root[label]) root[label] = {};
}
要调试代码,请在脚本标记中使用js创建一个简单的HTML文件,然后使用python-mhttp.server
从本地计算机提供该文件。然后,您可以添加一个调试点,并一步一步地检查代码。尝试将此作为一个整体解决方案:
const menu = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const deepMerge = (target, source) => {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source)) {
if (source[key] instanceof Object && key in target) Object.assign(source[key], deepMerge(target[key], source[key]))
}
// Join `target` and modified `source`
Object.assign(target || {}, source)
return target
};
const buildMenuMap = menu => {
return menu
.map(item => item.split('/').splice(1))
// The `root` value is the object that we will be merging all directories into
.reduce((root, directory) => {
// Iterates backwards through each directory array, stacking the previous accumulated object into the current one
const branch = directory.slice().reverse().reduce((acc, cur) => { const obj = {}; obj[cur] = acc; return obj;},null);
// Uses the `deepMerge()` method to stitch together the accumulated `root` object with the newly constructed `branch` object.
return deepMerge(root, branch);
}, {});
};
buildMenuMap(menu);
注意:深度合并解决方案取自您可以使用、&
buildMenuMap
该函数将数组作为输入,并将其简化为一个对象,其中对于数组中的每个条目,使用addLabelToMap
函数用相应的层次结构更新对象。每个条目都转换为一个级别数组(c.substring(1).split(“/”
)
addLabelToMap
该函数接受2个输入
- obj-当前根对象/节点
- ar-子层次结构的数组
和返回更新的对象
逻辑
- 函数弹出第一个值(
let key=ar.shift()
)作为键,并在对象中添加/更新(obj[key]=obj[key]| |{};
)
- 如果当前对象存在子层次结构(
If(ar.length)
),则递归调用函数更新对象直到结束(addLabelToMap(obj[key],ar)
)
- Else(无进一步的子层次结构),检查对象是否由于数组中的其他项而具有某种层次结构(
Else,如果(!object.keys(obj[key]).length)
)。如果没有层次结构,即它是一个叶,则将该值设置为null
(obj[key]=null
)注意,如果数组中永远不会有类似于/social/swipes/men/young
的条目以及现有条目,则else if
块可以简化为简单的else
块
- 对象已更新,返回最终更新的对象
让arr=['/social/swipes/women','/social/swipes/men','/upgrade/premium'];
函数addLabelToMap(obj,ar){
让key=ar.shift();
obj[key]=obj[key]|{};
如果(ar.length)添加标签标记映射(obj[键],ar);
如果(!Object.keys(obj[key]).length)obj[key]=null,则为else;
返回obj;
}
函数构建菜单映射(ar){
返回ar.reduce((a,c)=>addLabelToMap(a,c.substring(1).split(“/”),{});
}
日志(buildMenuMap(arr))代码>这个问题是关于创建对象并在通过输入循环时保持其状态的问题