tot*_*oob 14 javascript arrays javascript-objects ecmascript-6 arrow-functions
随着阵列
['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
Run Code Online (Sandbox Code Playgroud)
我想构建一个看起来像这样的地图对象:
{
'social': {
swipes: {
women: null,
men: null
}
},
'upgrade': {
premium: null
}
}
Run Code Online (Sandbox Code Playgroud)
const menu = ['/social/swipes/women', '/social/likes/men', '/upgrade/premium'];
const map = {};
const addLabelToMap = (root, label) => {
if(!map[root]) map[root] = {};
if(!map[root][label]) map[root][label] = {};
}
const buildMenuMap = menu => {
menu
// make a copy of menu
// .slice returns a copy of the original array
.slice()
// convert the string to an array by splitting the /'s
// remove the first one as it's empty
// .map returns a new array
.map(item => item.split('/').splice(1))
// iterate through each array and its elements
.forEach((element) => {
let root = map[element[0]] || "";
for (let i = 1; i < element.length; i++) {
const label = element[i];
addLabelToMap(root, label)
// set root to [root][label]
//root = ?
root = root[label];
}
});
}
buildMenuMap(menu);
console.log(map);Run Code Online (Sandbox Code Playgroud)
但我不确定如何切换价值root.
我该怎么设置root,以使之递归调用addLabelToMap与
'[social]','swipes' => '[social][swipes]','women' => '[social][swipes]','men'?
我已经习惯root = root[element]但是它给出了一个错误.
替代解决方案会很棒,但我想了解为什么这不能从根本上发挥作用.
Nit*_*ang 13
这个问题是关于创建对象并在循环遍历input数组和基于分割字符串时维护它的状态/.
这可以使用Array.reduce我们从空对象开始的地方来完成,当循环input开始我们开始填充它时,对于每个字符串中的最后一个字,我们将值赋给nullobject属性.
let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) => {
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) => {
k[v] = (i != keys.length - 1)
? k[v] || {}
: null
return k[v]
}, o)
return o
}, {})
console.log(output)Run Code Online (Sandbox Code Playgroud)
它很简单:
root = root[label];
Run Code Online (Sandbox Code Playgroud)
如果您将辅助函数更改为:
const addLabelToMap = (root, label) => {
if(!root[label]) root[label] = {};
}
Run Code Online (Sandbox Code Playgroud)
我把它写成:
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;
}
Run Code Online (Sandbox Code Playgroud)
用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));
Run Code Online (Sandbox Code Playgroud)
说明:
对于每一个item中menu排列,我们提取parts由第一摆脱主导的'/'(使用slice(1)),然后split通过廷'/'.
然后我们lastPart从这个结果数组中删除(最后一部分是从其余部分处理的).
对于parts数组中的每个剩余部分,我们遍历root数组.在每个遍历级别,我们要么返回该级别的对象(acc[part]如果它已经存在),要么我们创建并返回一个新对象(如果不存在)(acc[part] = {}).
在我们到达最后一级后leaf,我们使用lastPart将值设置为null.
请注意,我们传递Object.create(null)给reduce.Object.create(null)创建一个无原型的对象,这样可以更安全地使用,root[someKey]而无需检查是否someKey属于自有财产.
例:
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));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);Run Code Online (Sandbox Code Playgroud)
您也可以使用递归函数以简洁的方式解决此问题,如下所示:
let obj={}, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj={}) => {
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, {[prop]: {}})
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
}
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)Run Code Online (Sandbox Code Playgroud)
我们的想法是将每个路径传递给一个makeObj函数,该函数将递归地修饰一个对象,直到它到达路径数组的末尾.这是常用Array.reduce方法的另一种替代方案.
我只是调试你的代码,看看有什么不对,我敦促你做同样的事情.你犯了两个(明显的)错误:
首先,在最初的迭代,这里的值map只是一个空的对象{},价值root被初始化为""和label的swipes.
.forEach((element) => {
let root = map[element[0]] || "";
...
root = root[label];
}
Run Code Online (Sandbox Code Playgroud)
那么你得到的root[label]是undefined新的根undefined.
其次,你map到处都在使用它.
const addLabelToMap = (root, label) => {
if(!map[root]) map[root] = {};
if(!map[root][label]) map[root][label] = {};
}
Run Code Online (Sandbox Code Playgroud)
相反,您应该将其作为参数,以便您能够进行递归.
const addLabelToMap = (root, label) => {
if(!root[label]) root[label] = {};
}
Run Code Online (Sandbox Code Playgroud)
要调试代码,请在脚本标记中使用js创建一个简单的HTML文件,然后使用本地计算机从中提供python -m http.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);
Run Code Online (Sandbox Code Playgroud)
注意:深度合并解决方案取自GitHubGist上的@ahtcx
您可以使用Array.reduce,Object.keys和String.substring 来简化代码
buildMenuMap
该函数将数组作为输入并将其缩减为一个对象,对于数组中的每个条目,使用addLabelToMap函数使用相应的层次结构更新对象.每个条目都转换为一个levels(c.substring(1).split("/"))数组.
addLabelToMap
该功能需要2个输入
并返回更新的对象
逻辑
let key = ar.shift())作为键,并在object(obj[key] = obj[key] || {};)中添加/更新.if(ar.length))的子层次结构,则递归调用该函数以更新对象直到end(addLabelToMap(obj[key], ar)).else if(!Object.keys(obj[key]).length)由于数组中的其他条目而具有某种层次结构().如果没有层次结构,即它是叶子,则将值设置为null(obj[key] = null).注意,如果绝不会有情况下有在阵列中的条目等/social/swipes/men/young与现有以来,else if块可以被简化为一个简单的else块.let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar) {
let key = ar.shift();
obj[key] = obj[key] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
function buildMenuMap(ar) {
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), {});
}
console.log(buildMenuMap(arr));Run Code Online (Sandbox Code Playgroud)
来自赏金描述的Citate:
当前答案中没有包含足够的细节.
我想你不明白它在目前的答案中是如何运作的.因此,我将为您提供两种解决方案:一种替代解决方案和一种具有Array.reduce()功能的扩展解决方案.
带for循环的替代解决方案
代码说明见代码注释.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = {};
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
console.log(JSON.stringify(result, null, 4));Run Code Online (Sandbox Code Playgroud)
但是如果你不理解它,那么看看这段代码:
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = {},
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = {};
curObj[parts[1]] = parts[1+1] ? {} : null;
console.log(JSON.stringify(result, null, 4));Run Code Online (Sandbox Code Playgroud)
扩展解决方案 Array.reduce()
在这个解决方案中,我使用扩展版本的用户Nitish Narang的代码,在注释和控制台输出中有一些解释 - 所以你可以在控制台中看到代码的作用.我的建议:如果你不理解带有箭头函数的代码,那么用正常的函数和适当的变量名称来完整地写出它来解释自己.我们(人类)需要一些图片来想象所有事物.如果我们只有一些简短的变量名称,那么想象一下这并不是很难.我还有点"短路"他的代码.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
{
console.log('\n' + curIdx + ': ------------\n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
{
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] || {}
: null;
return acc1[currentValue]
}, acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
}, {}); // {} - initialValue is empty object
console.log('\n------result------\n'
+ JSON.stringify(result, null, 4));Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
536 次 |
| 最近记录: |