我有这个任务。我有一个对象,它可以包含任何属性值 - 数组,对象,原始值,本身,并且可以具有任意嵌套属性(不要关心符号)。
我需要建立一个像这样的对象属性的地图。
const obj = {
p1: 'this',
p2: ['that'],
p3: { q: 'other' }
}
转为=>
["p1", "this"]
["p2/0", "that"]
["p3/q", "other"]
我创建的代码可以处理这些东西,任意嵌套,直接循环引用以及某种程度的间接循环引用(该部分需要一些调整)
// holds result (path: value)
const result = new Map();
// holds already visited objects
// used later to check for circular references
const visited = new WeakMap();
const objTravesal = (o, path = '') => {
const keys = Object.keys(o);
keys.forEach(key => {
// if the value is not an object
// add it into results
if (!(typeof o[key] === 'object')) {
result.set((path + String(key)), o[key]);
} else {
// if the object has not been visited yet
// or if has a same path prefix, proceed
if (!visited.has(o) || visited.get(o) === path) {
// prevent direct circular reference
if (o !== o[key]) {
visited.set(o, path);
objTravesal(o[key], path + key + '/');
}
}
}
});
}
const meta = {
info: '...'
}
meta.self = meta; // direct circular reference to meta
const obj = {
label: 'myObj',
metaInfo: meta,
count: 3,
additonal: {
main: ['main1', 'main2'],
secondary: ['secondar1', 'secondary2']
}
}
obj.self = obj; // direct circular reference to obj
// here is the problem
obj.other = meta;
meta.other = obj;
objTravesal(obj);
console.log([...result]);
运行时,它处理间接循环引用,因为它不会压碎,但记录的次数超出了我的喜好。
E.g。在这种特殊情况下(输出的一部分)。
// this is ok
["label", "myObj"]
// this should not be included as the information is already there
["metaInfo/other/label", "myObj"]
有没有人知道如何/在何处更改上述代码中的任何内容才能实现此目的?
答案 0 :(得分:2)
我认为您打算测试if (!visited.has(o[key]))
而不是if (!visited.has(o))
。这同样适用于visisted.set
- 您应该使用您正在查看的新对象,而不是旧对象。
总之,您甚至不需要Map
来存储路径,Set
足以收集访问过的对象。通过将类型检查和visited
检查移动到函数的前面,您可以节省很多麻烦,作为递归的基本情况:
function traverse(val, path = '', result = new Map, visited = new WeakSet) {
if (typeof val != 'object' || val == null) {
result.set(path, val);
} else /* it's an object */ if (!visited.has(val)) {
visited.add(val);
for (const key in val) {
traverse(val[key], path + '/' + key, result, visited);
}
}
return result;
}
如果您的对象结构具有对同一对象的多个引用(不一定是圆形),并且您想要输出最短路径,则需要使用bread-first traversal而不是当前深度 - 第一个。