我有一个如下的路径数组:
/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。
从以前的topic中,我发现我可以使用以下函数来获得类似的结果:
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];
}
}
将这些字符串转换为该数据结构的最佳方法是什么?
4条答案
按热度按时间jecbmhm31#
这是我想到的解决方案。它的工作原理是一次构建一个路径,然后将其与现有的数据结构进行比较。它还应该自己处理文件,因为你最初的帖子似乎暗示这是必要的。我决定最后将其分为两个函数,因为这可能会更容易解释。
代码:
说明:
这比我开始工作时想象的要复杂得多(也有趣得多)。一旦开始连接目录,操作的顺序就真的很重要了。
从
buildStructure
开始,我们Map路径数组以捕获任何没有前导斜杠的条目,然后根据它们引用的目录数对其进行排序,这样我们就可以确定我们是从结构的顶部向下工作的。将每个路径分隔成一个节点数组,然后弹出文件字符串,剩下的内容如下:
现在我们必须通过
findDirectory
来传递这些节点,以找到/创建文件的位置,变量pointer
用于跟踪我们在structure
对象中的位置,并且我们对指针所做的任何更改都将在结构中复制,因为它们共享引用等式。findDirectory
函数递归地处理每个节点,逐渐地将路径构建回它的全长。每当我们创建一个已经存在于structure
目录中的路径时,我们就移动到它的内部,并重新开始构建路径,以尝试找到下一个路径。如果我们找不到它,那么我们就得到了一个全新的目录。目标是当我们退出函数时总是在正确的目录中结束--如果需要的话,在此过程沿着创建它。为简化起见,假设我们只有两条路径要记录:
对于第一条路径,
findDirectory
将执行三次遍历,每次遍历时将为其提供以下参数:我们没有找到匹配的路径,所以当函数退出时,它会在
structure.directories
上创建该目录。现在,第二个路径将执行四次遍历:正如你所看到的,在第二次传递中,我们创建了字符串
/doc/data
,它确实存在于structure.directories
中。所以我们进入它,因为有更多的节点要处理,我们在那里创建了一个新的目录对象,并输入它。如果没有更多的节点要处理,我们就知道我们已经到达了正确的层次,这是不必要的。从这里开始,这是一个简单的情况,重新建立路径,并重复这个过程。一旦我们找到了正确的目录,我们就可以直接把文件放在指针上,它将被注册到结构中,一旦我们移动到下一个路径,指针将再次指向
structure.directories
。如果没有要处理的节点(仅文件名),则传递整个structures对象
findDirectory
,文件将进入对象的顶层。希望这能很好地解释问题,并对你有用。我很喜欢在这方面的工作,并会很高兴对如何改进它的任何建议。
enxuqcxy2#
这个挑战真的不是那么微不足道的。然而,该方法与人们可以考虑的、易于阅读和理解的以及因此可维护的子任务一起工作,以达到OP的目标...
...从上面的日志中可以看到,这些任务是...
清理和(重新)构建/Map
1.通过移除前导斜线的可能序列来净化/规范化每条路径。
1.构建文件数据项的列表,对于每个项,该列表包含X1 M0 N1 X和对应路径项的X1 M1 N1 X,其为后者的净化/规格化形式。
例如,
'/doc/data/dandu/sdasa.js'
Map到...分类
排序是通过比较两个当前Map的文件数据项的特性来完成的,方法如下...
1.按
pathName
进行比较1.按
fileName
比较,无扩展名1.按文件扩展名比较
因此原始文件列表看起来像这样...
......将被(清理/规范化Map并)排序为类似于......
排序是最基本的,因为后面的算法依赖于整齐排序/对齐的
pathName
。拆分为路径片段并将其聚类
为了保持这个任务 * 死愚蠢 *,它是由一个Map过程来完成的,这个过程不仅使用当前处理的项,而且使用这个项的前一个兄弟(或前导)。
通过将当前
pathName
与之前的pathName
拆分,将构建额外的pathPartials
列表。例如,
'foo/bar/baz'
将被拆分因此,'bar/baz'
已经是一个聚集的部分路径,该路径将用于通过将该部分连接到其先前兄弟的pathPartials
列表(此时为['foo']
)来创建当前文件数据项的pathPartials
列表。因此,前者的结果将是['foo', 'bar/baz']
。对于
'foo/bar/baz/biz'
也是如此,它以前的路径名为'foo/bar/baz'
,以前的部分列表为['foo', 'bar/baz']
,拆分结果为'biz'
,新的部分列表为['foo', 'bar/baz', 'biz']
。上面的排序文件数据列表Map到这个新列表中...
汇编最终索引
最后一步是一个简单的列表缩减任务,因为此时已经完成了正确地拆分和聚类项的每个路径部分的最困难部分。
cpjpxq1n3#
我的方法循环遍历每个文件(路径),并在遍历目录并最终遍历文件时递归地构建目录结构。
递归函数接受currentNode,以便知道 currently 有什么,这样我们就可以确保向结构中 add,而不是覆盖任何内容。
它还接受currentPath,currentPath会逐渐接受第一个元素,这样你就只处理剩下的元素,这样我们就知道什么时候完成了(只剩下一个元素)。
这可能不是最快的方法,但它似乎是一个整体失去了比任何其他答案更容易理解。
fjaof16o4#
你可以用一个递归函数来完成它,记住这只是一个可能的解决方案,可能不是最好的。
“convert”函数需要所有路径的数组。
请记住,此解决方案不考虑其中没有文件的条目。