如何迭代JSON来获得每个所需值的路径?

mepcadol  于 2022-12-15  发布在  其他
关注(0)|答案(1)|浏览(84)

我正在尝试做一个文件资源管理器。我尝试的方法是使用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]);
        }
    }
}

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

efzxgjgh

efzxgjgh1#

  • (在我看来,将其作为重复项关闭的投票是错误的。建议的链接都没有处理类似的结构,节点名在对象字符串属性中,子节点在对象数组属性中。它们是关于对象层次结构所表示的树的。重新打开。)*

你的问题有一个主要的问题。你问的是 the path。但是可能有很多路径。毕竟,在一个名为“logo.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将多个Map的数组值转换为一个数组,我们还使用spread syntax轻松地将空数组或只包含一个元素的数组转换为返回数组中的一个新元素或什么都不包含,并且我们将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"]]的内容,然后将其 Package 在一个函数中,将它们连接到一起,形成您的格式。这也将给予我们机会将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'))

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

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')

现在它的接口与原来的函数相同,但它是在代码之上编写的,您可以轻松地适应其他情况。
我并不是特别建议使用最后一种格式,只是指出在不同的抽象级别上经常存在选项。

相关问题