繁体   English   中英

将对象数组渲染为树

[英]Render array of objects as tree

我有一个对象数组,其中每个元素都包含一个表示其在树形结构中位置的字符串。 根据该数据生成树视图(例如,使用基本的ul / li标签)的简单方法是什么?

事先不知道树结构的深度,因此解决方案可能是递归吗?

我正在使用React,但我想这个问题并不是特定于React的,因此通用JS甚至伪代码都会有很大帮助。

示例数据:

[
    {
        "name":  "banana",
        "path": "food.healthy.fruit",
        // ... may contain other parameters
    },
    {
        "name":  "apple",
        "path": "food.healthy.fruit"
    }
    {
        "name":  "carrot",
        "path": "food.healthy.vegetable"
    },
    {
        "name":  "bread",
        "path": "food"
    },
    {
        "name":  "burger"
        "path": "food.unhealthy"
    },
    {
        "name":  "hotdog"
        "path": "food.unhealthy"
    },
    {
        "name": "germany",
        "path": "country.europe"
    },
    {
        "name": "china",
        "path": "country.asia"
    }
]

所需结果:

<ul>
    <li>
    food
    <ul>
        <li>bread</li>
        <li>healthy
        <ul>
            <li>
            fruit
            <ul>
                <li>apple</li>
                <li>banana</li>
            </ul>
            </li>
            <li>
            vegetable
            <ul>
                <li>carrot</li>
            </ul>
            </li>
        </ul>
        </li>
        <li>
        unhealthy
        <ul>
            <li>burger</li>
            <li>hotdog</li>
        </ul>
        </li>
    </ul>
    </li>
    <li>
    country
    <ul>
        <li>
        europe
        <ul>
            <li>germany</li>
        </ul>
        </li>
        <li>
        asia
        <ul>
            <li>china</li>
        </ul>
        </li>
    </ul>
    </li>
</ul>
  • 餐饮
    • 面包
    • 健康
      • 水果
        • 苹果
        • 香蕉
      • 蔬菜
        • 胡萝卜
    • 不良
      • 汉堡包
      • 热狗
  • 国家
    • 欧洲
      • 德国
    • 亚洲
      • 中国

首先按路径分组。

您可以通过遍历源数据并使用点符号分隔每个项目的路径来实现此目的。 然后通过这样的键将每个项目存储在对象中

store[country] = store[country] || {}
store[country][europe] = store[country][europe] || []
store[country][europe].push(germany)

然后在根级别获取对象的所有键,然后递归渲染所有项目。 这是一些伪代码:

function render(store){

    let keys = Object.keys(store)

    let ul = document.createElement('ul')

    for (var i = 0; i < keys.length; i++){

      let key = keys[i]

      if (typeof store[key] === 'object') {

        let li = document.createElement('li')

        //create a branch, return it with our render function and append to current level
        li.appendChild(render(store[key]))

      } else {

         // create html presentation for all items under the current key

         let li = document.createElement('li')

      }

       ul.appendChild(li)

    } 

    return ul

}

首先,您需要将数据重组为嵌套组。 这是将数组简化为必要结构的方法:

const tree = data.reduce(function(prev, curr) {
  const branches = curr.path.split('.')
  let branch = prev
  let branchName

  while (branches.length) {
    branchName = branches.shift()
    let rootIndex = branch.length ? branch.findIndex(el => el.name === branchName) : -1

    if (rootIndex === -1) {
      let newBranch = {
        name: branchName,
        children: []
      }
      branch = branch[branch.push(newBranch) - 1].children
    } else {
      branch = branch[rootIndex].children
    }

    if (branches.length === 0) {
      branch.push({
        name: curr.name
      })
    }
  }

  return prev
}, [])

它将为您提供类似于以下的数组:

[
  {
    name: 'food',
    children: [
      {
        name: 'bread'
      },
      {
        name: 'healthy',
        children: [
          {
            name: 'fruit',
            children: [
              {name: 'bannana'},
              {name: 'apple'}
            ]
          }
        ]
      }
    ]
  },
  {
    name: 'country',
    children: [
      // ...
    ]
  }
]

之后,很容易创建将递归呈现分支的Tree组件:

const Tree = (props) => (
  <ul>
    {props.data.map((branch, index) => (
      <li key={index}>
        {branch.name}
        {branch.children && (
          <Tree data={branch.children} />  
        )}
      </li>
    ))}
  </ul>  
)

演示 查看下面的演示。

 const data = [{ "name": "banana", "path": "food.healthy.fruit" }, { "name": "apple", "path": "food.healthy.fruit" }, { "name": "carrot", "path": "food.healthy.vegetable" }, { "name": "bread", "path": "food" }, { "name": "burger", "path": "food.unhealthy" }, { "name": "hotdog", "path": "food.unhealthy" }, { "name": "germany", "path": "country.europe" }, { "name": "china", "path": "country.asia" }] const tree = data.reduce(function(prev, curr) { const branches = curr.path.split('.') let branch = prev let branchName while (branches.length) { branchName = branches.shift() let rootIndex = branch.length ? branch.findIndex(el => el.name === branchName) : -1 if (rootIndex === -1) { let newBranch = { name: branchName, children: [] } branch = branch[branch.push(newBranch) - 1].children } else { branch = branch[rootIndex].children } if (branches.length === 0) { branch.push({ name: curr.name }) } } return prev }, []) const Tree = (props) => ( <ul> {props.data.map((branch, index) => ( <li key={index}> {branch.name} {branch.children && ( <Tree data={branch.children} /> )} </li> ))} </ul> ) ReactDOM.render( <Tree data={tree} />, document.getElementById('demo') ); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="demo"></div> 

暂无
暂无

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

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