javascript - 将路径数组转换为数据结构
问题描述
我有一个这样的路径数组:
/doc/data/main.js
/doc/data/xl.js
/doc/data/dandu/sdasa.js
/mnt/data/la.js
我正在尝试构建以下结构:
{
"directories": {
"/doc/data": {
"directories": {
"dandu": {
"files": {
"sdasa.js": 1
}
}
},
"files": {
"main.js": 1,
"xl.js": 1
}
},
"/mnt/data": {
"directories": {},
"files": {
"la.js": 1
}
}
},
"files": {}
}
请忽略该示例中文件的值。将来我会为此分配更复杂的数据。当前值为 1。
从上一个主题中,我发现我可以使用以下函数来获得类似的东西:
var parsePathArray = function() {
var parsed = {};
for(var i = 0; i < paths.length; i++) {
var position = parsed;
var split = paths[i].split('/');
for(var j = 0; j < split.length; j++) {
if(split[j] !== "") {
if(typeof position[split[j]] === 'undefined')
position[split[j]] = {};
position = position[split[j]];
}
}
}
return parsed;
}
该解决方案的主要问题是它拆分了每个目录。但我不想拆分每个目录,而是获取至少包含一个文件的目录。例如,/doc
在我的示例中没有文件(只有目录 - /data
),所以我们继续它。我尝试稍微改变一下功能,但没有奏效:
var str = '';
for (var j = 0; j < split.length; j++) {
if (j < split.length - 1 && typeof this.files[str] === 'undefined') {
str += '/' + split[j];
continue;
}
if (str !== '') {
if (typeof this.files[str] === 'undefined')
this.files[str] = {};
this.files = this.files[str];
}
}
将这些字符串转换为该数据结构的最佳方法是什么?
解决方案
这是我想出的解决方案。它通过一次构建一条路径并将其与现有数据结构进行比较来工作。它还应该自己处理文件,因为您的原始帖子似乎暗示这是必要的。最后我决定把它分成两个函数,因为这样可能更容易解释。
编码:
const paths = [
'/doc/data/main.js',
'doc/data/xl.js',
'/etc/further/owy.js',
'/etc/further/abc.js',
'etc/mma.js',
'/mnt/data/it.js',
'/mnt/data/path/is/long/la.js',
'mnt/data/path/is/la.js',
'/doc/data/dandu/sdasa.js',
'/etc/i/j/k/l/thing.js',
'/etc/i/j/areallylongname.js',
'thing.js'
];
function buildStructure(paths) {
let structure = {
directories: {},
files: {}
};
const compare = (a, b) => {
return a.split('/').length - b.split('/').length;
};
[...paths]
.map(path => path = path.charAt(0) === '/' ? path : `/${path}`)
.sort((a, b) => compare(a, b)).forEach(path => {
const nodes = path.split('/').slice(1);
const file = nodes.pop();
let pointer = findDirectory(nodes[0] ? structure.directories : structure, '', [...nodes]);
pointer.files = pointer.files || {};
pointer.files = {
...pointer.files,
[file]: 1
};
});
return structure;
};
function findDirectory(pointer, subPath, nodes) {
if (nodes.length === 0) {
if (subPath) {
pointer[subPath] = {};
pointer = pointer[subPath];
};
return pointer;
};
let newPath = `${subPath}/${nodes[0]}`;
nodes.shift();
if (pointer[newPath]) {
pointer = pointer[newPath];
if (nodes.length >= 1) {
pointer.directories = pointer.directories || {};
pointer = pointer.directories;
};
newPath = '';
};
return findDirectory(pointer, newPath, nodes);
};
const structure = buildStructure(paths);
console.log(structure);
.as-console-wrapper { min-height: 100%!important; top: 0; }
说明:
这最终比我开始研究它时想象的要复杂得多(也更有趣)。一旦开始连接目录,操作顺序就很重要。
从 开始buildStructure
,我们映射路径数组以捕获没有前导斜杠的任何条目。然后,根据它们引用的目录数量对它们进行排序。这样我们就可以确定我们是从结构的顶部向底部工作的。
将每个路径分成一个节点数组,然后弹出文件字符串。你留下了这样的东西:
const nodes = ['doc', 'data'];
const file = 'main.js';
现在我们必须通过这些节点findDirectory
来查找/创建文件的位置。该变量pointer
用于跟踪我们在structure
对象中的位置,并且我们对指针所做的任何更改都将在结构中复制,因为它们共享引用相等。
该findDirectory
函数递归地处理每个节点以逐渐建立路径回到其全长。每当我们创建一个已经存在于structure
s 目录中的路径时,我们就会在其中移动并重新开始构建路径以尝试找到下一个路径。如果我们找不到它,那么我们就有了一个全新的目录。目的是当我们退出函数时总是在正确的目录中结束 - 如果需要,可以在此过程中创建它。
为简化起见,假设我们只有两条记录路径:
const paths = [
'doc/data/main.js',
'doc/data/dandu/sdasa.js'
];
对于第一条路径,findDirectory
将进行三遍。这些是每次传递时将提供给它的参数:
pointer = structure.directories > same > same
subPath = '' > '/doc' > '/doc/data'
nodes = ['doc', 'data'] > ['data'] > []
我们从来没有匹配过,所以当函数退出时,它会在structure.directories
. 现在,第二条路径将进行四次传递:
pointer =
structure.directories >
same >
structure.directories./doc/data.directories >
same
subPath = '' > '/doc' > '' > '/dandu'
nodes = ['doc', 'data', 'dandu'] > ['data', 'dandu'] > ['dandu'] > []
如您所见,在第二遍中,我们创建了/doc/data
确实存在于structure.directories
. 所以我们进入它,因为有更多的节点要处理,我们在那里创建一个新的目录对象并输入它。如果没有更多节点要处理,我们就知道我们已经到达了正确的级别,这将是不必要的。从这里开始,只需重新构建路径并重复该过程即可。
一旦我们在正确的目录中,我们可以将文件直接放在指针上,它将在结构上注册。一旦我们移动到下一条路径,指针将再次指向structure.directories
。
如果没有要处理的节点(仅文件名) -findDirectory
而是传递整个结构对象,文件将进入对象的顶层。
希望这可以很好地解释事情并对您有用。我很享受这方面的工作,并且很高兴收到有关如何改进它的任何建议。
推荐阅读
- excel - 用于突出显示行的 Excel 条件格式
- json - 使用 JSONPath 从 JSON 中提取值,不包括特定值
- javascript - highchart的columnrange图中的重叠条
- pandas - 根据信息最少的方式在 Pandas 中删除部分重复项
- java - Spring 5 不再向共享 entityManager 注入代理
- google-cloud-platform - 如何使用 gcloud 在实例上设置磁盘删除规则
- swift - Mandelbrot 迅速设置
- python-3.7 - “join”的打印输出中不需要的双反斜杠(Python 3.7.3)
- postgresql - 尝试使用 Postgres 设置 Adonis 时缺少数据库连接
- css - css img inset box-shadow 技巧 + 中心 (v+h) 锚点 + 最大高度