Как перебрать 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]);
        }
    }
}

К сожалению, я пока не смог дойти до какой-либо логики через итерацию.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

(Голосование за то, чтобы закрыть это как дубликат, показалось мне неправильным. Ни одна из предложенных ссылок не касалась структуры, подобной этой, с именами узлов в свойствах строки объекта и дочерними элементами в свойствах массива объектов. Они были о деревьях, представленных иерархия объектов. Открыто повторно.)


В вашем вопросе есть одна серьезная проблема. Вы спрашиваете путь. Но их может быть много. В конце концов, в файловой системе с именем «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 для преобразования нескольких сопоставленных значений массива в один массив по мере повторения. Мы также используем синтаксис распространения, чтобы удобно поворачивать либо пустой массив, либо массив, содержащий только один элемент, либо новый элемент в возвращаемом массиве, либо вообще ничего. И мы распространяем результат повторения на нашем 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')

Теперь он имеет тот же интерфейс, что и исходная функция, но написанный поверх кода, который можно легко адаптировать к другим обстоятельствам.

Я не предлагаю конкретно этот последний формат, просто указываю, что часто есть варианты на разных уровнях абстракции.

Большое спасибо!! Я застрял на нем надолго.

Kshitij Tyagi 07.12.2022 18:25

Другие вопросы по теме