【问题标题】:Render array of objects as tree将对象数组渲染为树
【发布时间】:2017-05-29 01:51:00
【问题描述】:

我有一个对象数组,其中每个元素都包含一个字符串,表示它在树结构中的位置。根据该数据生成树视图(例如使用基本的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>
  • 食物
    • 面包
    • 健康
      • 水果
        • 苹果
        • 香蕉
      • 蔬菜
        • 胡萝卜
    • 不良
      • 汉堡
      • 热狗
  • 国家
    • 欧洲
      • 德国
    • 亚洲
      • 中国

【问题讨论】:

    标签: javascript arrays reactjs tree


    【解决方案1】:

    首先按路径分组。

    您可以通过遍历源数据并按点符号拆分每个项目的路径来做到这一点。然后通过像这样的键将每个项目存储在一个对象中

    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
    
    }
    

    【讨论】:

      【解决方案2】:

      首先,您需要将数据重组为嵌套组。以下是如何将数组简化为必要的结构:

      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>

      【讨论】:

        猜你喜欢
        • 2017-09-20
        • 1970-01-01
        • 2019-11-15
        • 2019-04-23
        • 1970-01-01
        • 1970-01-01
        • 2020-03-03
        • 1970-01-01
        • 2023-01-13
        相关资源
        最近更新 更多