如何通过JSON进行迭代以获得每个所需值的路径



我正在尝试制作一个文件资源管理器。我尝试采用的方法是使用JSON数据。我制作了一个JSON演示,以供参考数据的处理方式。我试图做的是从数据中创建每个目录和文件的路径。我知道我必须在其中使用迭代,但不知道如何获得路径。

{
"project-name": "name of the project",
"author": "username of the author",
"date": "DD-MM-YYYY",
"privacy": "public / private",
"collaborators": [
"email-address / user-id of Collaborator-1",
"email-address / user-id of Collaborator-2"
],
"plan": "active-plan-name",
"database": [
{
"type": "directory",
"name": "js",
"items": [
{
"type": "directory",
"name": "assets",
"items": [
{
"type": "directory",
"name": "icons",
"items": [
{
"type": "file",
"name": "logo.png",
"content": "path of logo.png"
}
]
}
]
},
{
"type": "directory",
"name": "lib",
"items": [
{
"type": "file",
"name": "jquery.min.js",
"content": "CONTENT OF jquery.min.js"
},
{
"type": "file",
"name": "split.js",
"content": "CONTENT OF split.js"
}
]
},
{
"type": "directory",
"name": "src",
"items": [
{
"type": "file",
"name": "script.js",
"content": "CONTENT OF script.js"
}
]
}
]
},
{
"type": "directory",
"name": "style",
"items": [
{
"type": "file",
"name": "main.css",
"content": "CONTENT OF main.css"
}
]
},
{
"type": "file",
"name": "index.html",
"content": "CONTENT OF index.html"
}
]
}

以上是我的JSON数据供参考,文件和目录数据从数据库键开始。

示例:对于文件logo.png,我希望路径返回为js/assets/icons/logo.png

它反复遍历JSON数据,并期望用它构建一些逻辑。我为它写的函数是,

function iterate(obj){
for(prop in obj){
if(typeof(obj[prop]) == "object){
iterate(obj[prop]);
}
}
}

不幸的是,到目前为止,我还无法通过迭代找到任何逻辑。


您的问题有一个主要问题。您请求路径。但他们中可能有很多人。毕竟,在名为"的文件系统中可能存在多个文件;徽标.png";。所以我们可以试着找到第一场比赛,或者试着找到所有的比赛。此解决方案假定您想要所有。

const pathsTo = (xs, target, path = '') => 
xs .flatMap (({name, items = []}) => [
... (name == target ? [`${path}/${name}`] : []),
... pathsTo (items, target, `${path}/${name}`)
])
const input = {"project-name": "name of the project", author: "username of the author", date: "DD-MM-YYYY", privacy: "public / private", collaborators: ["email-address / user-id of Collaborator-1", "email-address / user-id of Collaborator-2"], plan: "active-plan-name", database: [{type: "directory", name: "js", items: [{type: "directory", name: "assets", items: [{type: "directory", name: "icons", items: [{type: "file", name: "logo.png", content: "path of logo.png"}]}]}, {type: "directory", name: "lib", items: [{type: "file", name: "jquery.min.js", content: "CONTENT OF jquery.min.js"}, {type: "file", name: "split.js", content: "CONTENT OF split.js"}]}, {type: "directory", name: "src", items: [{type: "file", name: "script.js", content: "CONTENT OF script.js"}]}]}, {type: "directory", name: "style", items: [{type: "file", name: "main.css", content: "CONTENT OF main.css"}]}, {type: "file", name: "index.html", content: "CONTENT OF index.html"}]}
console .log (pathsTo (input .database, 'logo.png'))

当我们重复时,我们使用Array.prototype.flatMap将多个映射的数组值转换为单个数组。我们还使用spread语法轻松地转向一个空数组或一个只包含一个项的数组,放入一个新的项in out返回的数组中,或者什么都没有。并将items上的递归结果推广到我们的结果中。

这引入了一个您可能不想要的工件。当我们想要"js/assets/icons/logo.png"(没有前导斜杠)时,结果数组中的一个条目看起来像"/js/assets/icons/logo.png"。保留斜杠有很好的理由,但如果我们不想,我们可以在事后通过简单的results .map (r => r .slice(1))来解决这个问题。但是,让我们看看如何内联实现这一点。在添加斜杠之前,我们只需要测试路径是否为空。类似path + (path ? '/' : '') + name。但是,在我们使用${path}/${name}的两个地方,这感觉太多的代码无法重复,所以让我们将其提升一个级别。

我们可以将它作为默认参数添加到flatMap中,但这会增加一些丑陋,因为我们必须在两个不需要的参数之后添加它(索引和整个数组被传递到flatMap的回调,但我们忽略了它们。)然而,我们已经在破坏第一个参数,所以我们可以简单地将计算添加为其中的默认部分。它可能看起来像这样:

const pathsTo = (xs, target, path = '') => 
xs .flatMap (({name, items = [], newPath = path + (path ? '/' : '') + name}) => [
... (name == target ? [newPath] : []),
... pathsTo (items, target, newPath)
])
const input = {"project-name": "name of the project", author: "username of the author", date: "DD-MM-YYYY", privacy: "public / private", collaborators: ["email-address / user-id of Collaborator-1", "email-address / user-id of Collaborator-2"], plan: "active-plan-name", database: [{type: "directory", name: "js", items: [{type: "directory", name: "assets", items: [{type: "directory", name: "icons", items: [{type: "file", name: "logo.png", content: "path of logo.png"}]}]}, {type: "directory", name: "lib", items: [{type: "file", name: "jquery.min.js", content: "CONTENT OF jquery.min.js"}, {type: "file", name: "split.js", content: "CONTENT OF split.js"}]}, {type: "directory", name: "src", items: [{type: "file", name: "script.js", content: "CONTENT OF script.js"}]}]}, {type: "directory", name: "style", items: [{type: "file", name: "main.css", content: "CONTENT OF main.css"}]}, {type: "file", name: "index.html", content: "CONTENT OF index.html"}]}
console .log (pathsTo (input .database, 'logo.png'))

这解决了我们的问题。但我建议我们更进一步,使用更通用的中间格式。让我们的递归函数返回类似[["js", "assets", "icons" "logo.png"]]的内容,然后将其封装在一个函数中,该函数将这些内容连接到您的格式中。这也将使我们有机会将input.datbase从调用代码转移到我们的主函数中。(在递归版本中,我们不能轻易做到这一点,因为外部对象没有相同的递归结构。)

const _pathsTo = (xs, target, path = []) => 
xs .flatMap (({name, items = [], newPath = path .concat (name)}) => [
... (name == target ? [newPath] : []),
... _pathsTo (items, target, newPath)
])
const pathsTo = (xs, target) => 
_pathsTo (xs .database, target) .map (ns => ns .join ('/'))
const input = {"project-name": "name of the project", author: "username of the author", date: "DD-MM-YYYY", privacy: "public / private", collaborators: ["email-address / user-id of Collaborator-1", "email-address / user-id of Collaborator-2"], plan: "active-plan-name", database: [{type: "directory", name: "js", items: [{type: "directory", name: "assets", items: [{type: "directory", name: "icons", items: [{type: "file", name: "logo.png", content: "path of logo.png"}]}]}, {type: "directory", name: "lib", items: [{type: "file", name: "jquery.min.js", content: "CONTENT OF jquery.min.js"}, {type: "file", name: "split.js", content: "CONTENT OF split.js"}]}, {type: "directory", name: "src", items: [{type: "file", name: "script.js", content: "CONTENT OF script.js"}]}]}, {type: "directory", name: "style", items: [{type: "file", name: "main.css", content: "CONTENT OF main.css"}]}, {type: "file", name: "index.html", content: "CONTENT OF index.html"}]}
console .log (pathsTo (input, 'logo.png'))

我们可以更进一步,通过任意谓词搜索,而不是搜索名称。这段代码只涉及稍微多一点,它增加了很大的灵活性。在这个抽象级别上,我可能会将数据库属性的查找移回调用者。

const _pathsTo = (pred) => (xs, path = []) => 
xs .flatMap (({name, items = [], newPath = path .concat (name)}) => [
... (pred (name) ? [newPath] : []),
... _pathsTo (pred) (items, newPath)
])
const pathsTo = (pred) => (xs) => 
_pathsTo (pred) (xs) .map (ns => ns .join ('/'))
const input = {"project-name": "name of the project", author: "username of the author", date: "DD-MM-YYYY", privacy: "public / private", collaborators: ["email-address / user-id of Collaborator-1", "email-address / user-id of Collaborator-2"], plan: "active-plan-name", database: [{type: "directory", name: "js", items: [{type: "directory", name: "assets", items: [{type: "directory", name: "icons", items: [{type: "file", name: "logo.png", content: "path of logo.png"}]}]}, {type: "directory", name: "lib", items: [{type: "file", name: "jquery.min.js", content: "CONTENT OF jquery.min.js"}, {type: "file", name: "split.js", content: "CONTENT OF split.js"}]}, {type: "directory", name: "src", items: [{type: "file", name: "script.js", content: "CONTENT OF script.js"}]}]}, {type: "directory", name: "style", items: [{type: "file", name: "main.css", content: "CONTENT OF main.css"}]}, {type: "file", name: "index.html", content: "CONTENT OF index.html"}]}
console .log (pathsTo (name => name == 'logo.png') (input .database, 'logo.png'))
console .log (pathsTo (name => name .endsWith ('.js')) (input .database, 'logo.png'))

我们,如果我们想要回到我们初始函数的等价物,我们可以在这个版本的顶部写下这个:

const pathsToName = (input, target) => 
pathsTo (name => name == target) (input .database)
pathsToName (input, 'split.js')

现在,它具有与原始函数相同的接口,但编写在代码之上,您可以很容易地适应其他情况。

我并不是特别建议最后一种格式,只是指出在不同的抽象级别上通常有选项。

最新更新