渲染对象数组作为树

时间:2017-01-13 16:10:26

标签: javascript arrays reactjs 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>
        
  •     餐饮     
              
    • 面包
    •         
    • 健康         
                    
      •             水果             
                          
        • 苹果
        •                 
        • 香蕉
        •             
                    
      •             
      •             蔬菜             
                          
        • 胡萝卜
        •             
                    
      •         
              
    •         
    •         不良         
                    
      • 汉堡
      •             
      • 热狗
      •         
              
    •     
        
  •     
  •     国家     
              
    •         欧洲         
                    
      • 德国
      •         
              
    •         
    •         亚洲         
                    
      • 中国
      •         
              
    •     
        

2 个答案:

答案 0 :(得分:0)

首先按路径分组。

您可以通过点符号迭代每个项目的源数据和拆分路径来完成此操作。然后通过像这样的键

将每个项目存储在一个对象中
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

}

答案 1 :(得分:0)

首先,您需要将数据重组为嵌套组。以下是如何将阵列减少到必要的结构:

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>