Пытаюсь сделать файловый менеджер. Подход, который я пытаюсь использовать, - это данные 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]);
}
}
}
К сожалению, я пока не смог дойти до какой-либо логики через итерацию.
(Голосование за то, чтобы закрыть это как дубликат, показалось мне неправильным. Ни одна из предложенных ссылок не касалась структуры, подобной этой, с именами узлов в свойствах строки объекта и дочерними элементами в свойствах массива объектов. Они были о деревьях, представленных иерархия объектов. Открыто повторно.)
В вашем вопросе есть одна серьезная проблема. Вы спрашиваете путь. Но их может быть много. В конце концов, в файловой системе с именем «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')
Теперь он имеет тот же интерфейс, что и исходная функция, но написанный поверх кода, который можно легко адаптировать к другим обстоятельствам.
Я не предлагаю конкретно этот последний формат, просто указываю, что часто есть варианты на разных уровнях абстракции.
Большое спасибо!! Я застрял на нем надолго.