Учитывая этот простой массив объектов:
var allPaths = [
{path: "/", component: 'cmp-1'},
{path: "/my-cool-list/", component: 'cmp-2'},
{path: "/my-cool-list/this-is-card-1", component: 'cmp-3'},
{path: "/my-cool-list/this-is-card-2", component: 'cmp-4'},
{path: "/blog/", component: 'cmp-5'},
{path: "/blog/2017/01/this-is-card-3", component: 'cmp-6'},
{path: "/blog/2017/02/this-is-card-4", component: 'cmp-7'},
{path: "/recettes/", component: 'cmp-8'},
{path: "/recettes/this-is-card-5", component: 'cmp-9'},
{path: "/recettes/this-is-card-6", component: 'cmp-10'}
]
Нужно произвести этот вывод:
[
{
path: "/", component: 'cmp-1'
},
{
path: "/my-cool-list",
component: 'cmp-2',
children: [
{path: "/this-is-card-1", component: 'cmp-3'},
{path: "/this-is-card-2", component: 'cmp-4'}
]
},
{
path: "/blog",
component: 'cmp-5',
children: [
{
path: "/2017",
children: [
{
path: "/01",
children: [
{path: "/this-is-card-3", component: 'cmp-6'}
]
},
{
path: "/02",
children: [
{path: "/this-is-card-4", component: 'cmp-7'}
]
}
]
}
]
},
{
path: "/recettes",
component: 'cmp-8',
children: [
{path: "/this-is-card-5", component: 'cmp-9'},
{path: "/this-is-card-6", component: 'cmp-10'}
]
},
]
Один из способов, который я нашел для этого, - это:
allPaths
tree
tree
в уникальное.Код выглядит примерно так скрипку здесь:
let tree = []
// Recursive Tree Builder
const buildTree = (pathArray, ...others) => {
var currentToken = pathArray.shift()
var arr = []
if (pathArray.length>0) {
arr.push( {path: '/'+currentToken, children: []} )
arr[0].children = buildTree(pathArray, ...others)
} else {
arr.push( Object.assign( {path: '/'+currentToken}, others[0] ))
}
return arr;
}
// For each object in 'allPaths', build a tree
allPaths.map( page => {
let {path, ...rest} = page
let res = buildTree(path.split('/').slice(1), rest)
tree.push(res[0])
})
// external code to deeply merge each objects in 'tree' (external library: no need to put the code here but you got the point)
Я предполагаю, что должен быть способ построить конечный результат лениво, не требуя еще одной операции слияния в конце: например, построить окончательное дерево на ходу без промежуточного хранения + другую операцию.
Чтобы сузить вопрос дальше, я попытался построить дерево "на лету", но я застрял, пытаясь надавить на точное местоположение в дереве. Например, сохранение ссылки на tree[0].children[0].children[0].children[0]...
меня tree[0].children[0].children[0].children[0]...
поскольку я не знаю, насколько глубже я узнаю связанный путь, или если children
будут существуют на этом уровне.
Псевдокод (я думаю), будет:
allPaths
один за другим (притворяться, что первый пройденный объект имеет путь как /blog/level-2
)
найдите tree
для 1-го уровня, найденного в path
(например: /blog
in /blog/level-2
)
не найдено? затем, нажмите объект в дереве
этот путь имеет дополнительные уровни (например, в /blog/level-2
)?
.......да? идти на один уровень глубже (/level-2
) в children
и повторять с шага 3
....... нет? уровень сброса до 0. break
Теперь у меня возникают трудности с отслеживанием этих уровней: - Я не знаю, как легко запросить tree
учетом пути: /blog/level-2
может быть у tree[2].children[14]
или tree[32].children[57]
и мне сложно запросить его, но более того, чтобы STORE эту информацию для дальнейшего использования.
Какие-нибудь мысли?
Хорошо, я понял это сам.
const appendToTree = (tree, obj) => {
// extract path from object
let {path, ...rest} = obj
// turn path into array of params
let URLparams = path.split('/').slice(1)
// recursive calls could send empty array
if (URLparams.length === 0) return tree
// take out first element in array: removes it from the array as well
let currentURLparam = URLparams.shift()
// reset path to what left in URLparams after shift
path = '/' + URLparams.join('/')
let currentPosition = 0,
currentNode = {},
found = false
loopTree: // find corresponding param in tree
for(currentPosition; currentPosition < tree.length; currentPosition++) {
if (tree[currentPosition].path === '/'+currentURLparam) {
currentNode = tree[currentPosition]
found = true
break loopTree
}
}
if( found ) { // found a matching path
// get a reference to children or create an empty one
currentNode['children'] = currentNode.children || []
// repeat all, but with current reference and val which has been shifted before
return appendToTree(currentNode.children, {path, ...rest})
} else { // not matching path found: insert it
if (URLparams.length > 0) { // still have URL params left ?
// push current page object into array
// Here we could force any default props to index levels
let pos = tree.push({path: '/'+currentURLparam, children: []}) - 1
// we still have params to process, so we proceed another iteration
return appendToTree(tree[pos].children, {path, ...rest})
} else { // no URL params left, this is the last one in stack
tree.push({path: '/'+currentURLparam, ...rest})
return tree
}
}
}
const buildTree = (tree, pages) => {
// Traverse all pages objects and append each to provided tree
pages.map(page => {
appendToTree(tree, page)
})
return tree
}
Тогда нам просто нужно позвонить:
buildTree([], allPaths)
... он будет правильно создавать дерево "на лету", когда мы пересекаем каждый объект в allPaths
. Чтобы решить мою проблему, я использовал рекурсию.