简体   繁体   English

如何从 JavaScript 中带有缩进的平面列表构建树?

[英]How to build a tree from a flat list with indentation in JavaScript?

I always struggle when faced with this problem, and it takes some serious work.面对这个问题时,我总是很挣扎,这需要一些认真的工作。 I am currently trying to solve this problem and it will probably take me a few days.我目前正在尝试解决这个问题,可能需要几天时间。 Wanted to see if you have a system or simple way of solving this problem.想看看您是否有解决此问题的系统或简单方法。

Basically, say you have a flat list of DOM nodes with padding left indentation at steps of 15px.基本上,假设您有一个 DOM 节点的平面列表,其中以 15px 的步长向左填充缩进。 Visually, it forms a tree like a file browser.在视觉上,它 forms 一棵树状文件浏览器。 But structurally in the DOM, it is implemented as a flat list.但在 DOM 的结构上,它是作为平面列表实现的。 How can you then iterate through the list and build a tree?然后如何遍历列表并构建树?

<div style='padding-left: 0px'>A</div>
<div style='padding-left: 15px'>AA</div>
<div style='padding-left: 15px'>AB</div>
<div style='padding-left: 30px'>ABA</div>
<div style='padding-left: 30px'>ABB</div>
<div style='padding-left: 45px'>ABBA</div>
<div style='padding-left: 45px'>ABBB</div>
<div style='padding-left: 45px'>ABBC</div>
<div style='padding-left: 30px'>ABC</div>
<div style='padding-left: 15px'>AC</div>
<div style='padding-left: 0px'>B</div>
<div style='padding-left: 0px'>C</div>
...

That should then become a JSON tree like this:那应该变成像这样的 JSON 树:

[ 
  {
    title: 'A',
    children: [
      {
        title: 'AA',
        children: []
      },
      {
        title: 'AB',
        children: [
          {
            title: 'ABA',
            children: []
          },
          {
            title: 'ABB',
            children: [
              {
                title: 'ABBA',
                children: []
              },
              {
                title: 'ABBB',
                children: []
              },
              {
                title: 'ABBC',
                children: []
              }
            ]
          },
          {
            title: 'ABC',
            children: []
          }
        ]
      },
      {
        title: 'AC'
      }
    ]
  },
  {
    title: 'B',
    children: []
  },
  {
    title: 'C',
    children: []
  }
]

How do you do this?你怎么做到这一点? I get lost:我迷路了:

let tree = []
let path = [0]

let items = list('div')

items.forEach(item => {
  let left = parseInt(item.style[`padding-left`] || 0) % 15
  let set = tree
  let p = path.concat()
  while (left) {
    let x = p.shift()
    set[x] = set[x] || { children: [] }
    set = set[x].children
    left--
  }
})

function list(s) {
  return Array.prototype.slice.call(document.querySelectorAll(s))
}

It's a stack since it's sequential.它是一个堆栈,因为它是顺序的。 Something like this?是这样的吗?

We assume the folder structure is fully "expanded," therefore the parent of each folder (except the lowest, for which the parent is the root) must have been examined before the current one.我们假设文件夹结构是完全“展开”的,因此必须在当前文件夹之前检查每个文件夹的父文件夹(最低文件夹除外,父文件夹是根文件夹)。 The parent must also have a lower "padding-left" assignment.父级还必须具有较低的“padding-left”分配。

ptrs is a stack to which we append a reference to the next examined folder. ptrs是一个堆栈,我们 append 是对下一个检查文件夹的引用。 The folder at the top (the end) of the stack is the last folder we examined.堆栈顶部(末尾)的文件夹是我们检查的最后一个文件夹。 If those folders at the top of the stack have a higher than or equal "padding-left" assignment, they couldn't possibly be the parent of the current folder;如果堆栈顶部的那些文件夹具有高于或等于“padding-left”分配,则它们不可能是当前文件夹的父级; and we can't possibly have more children of those after the current folder so we remove (pop) them until we find the last folder placed that had a lower "padding-left."在当前文件夹之后我们不可能有更多的孩子,所以我们删除(弹出)它们,直到我们找到最后一个放置的文件夹具有较低的“padding-left”。

 function getData(s){ const left = +s.match(/\d+/)[0]; const title = s.match(/[AZ]+/)[0]; return [left, title]; } function f(divs){ const tree = { title: 'root', children: [] }; const ptrs = [[0, tree]]; // stack for (let str of divs){ const [left, title] = getData(str); while (ptrs.length && ptrs[ptrs.length-1][0] >= left) ptrs.pop(); parent = ptrs.length? ptrs[ptrs.length-1][1]: tree; const obj = {title: title, children: []}; parent.children.push(obj); ptrs.push([left, obj]); } return tree; } var divs = [ "<div style='padding-left: 0px'>A</div>", "<div style='padding-left: 15px'>AA</div>", "<div style='padding-left: 15px'>AB</div>", "<div style='padding-left: 30px'>ABA</div>", "<div style='padding-left: 30px'>ABB</div>", "<div style='padding-left: 45px'>ABBA</div>", "<div style='padding-left: 45px'>ABBB</div>", "<div style='padding-left: 45px'>ABBC</div>", "<div style='padding-left: 30px'>ABC</div>", "<div style='padding-left: 15px'>AC</div>", "<div style='padding-left: 0px'>B</div>", "<div style='padding-left: 0px'>C</div>" ] console.log(f(divs));

Interesting exercise.有趣的练习。 Here's another approach that is a bit more verbose than the prior solution but also works with dom nodes rather than string html这是另一种方法,它比之前的解决方案更冗长,但也适用于 dom 节点而不是字符串 html

 const buildTree = (selector) => { const elems = [...document.querySelectorAll(selector)].map((el,i)=>({el, title: el.textContent, idx:i, inset: parseInt(el.style.paddingLeft)})); const getChildren = ({inset:pInset, idx:start}) => { const nextParentIdx = elems.findIndex(({inset, idx}, i)=> inset <= pInset && i >start); const desc = elems.slice(start, nextParentIdx+1 ).filter(({inset})=>inset-pInset === 15); return desc.map(getItem); } const getItem = (o)=>{ return {title: o.title, children: getChildren(o)} } return elems.filter(({inset})=>.inset).map(getItem) } console.log(JSON,stringify(buildTree('div'),null, 4))
 .as-console-wrapper { max-height: 100%;important:top;0;}
 <div style='padding-left: 0px'>A</div> <div style='padding-left: 15px'>AA</div> <div style='padding-left: 15px'>AB</div> <div style='padding-left: 30px'>ABA</div> <div style='padding-left: 30px'>ABB</div> <div style='padding-left: 45px'>ABBA</div> <div style='padding-left: 45px'>ABBB</div> <div style='padding-left: 45px'>ABBC</div> <div style='padding-left: 30px'>ABC</div> <div style='padding-left: 15px'>AC</div> <div style='padding-left: 0px'>B</div> <div style='padding-left: 0px'>C</div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM