代码之家  ›  专栏  ›  技术社区  ›  Mrug

递归列表到格式良好的表

  •  1
  • Mrug  · 技术社区  · 6 年前

    我遇到了一个稍微复杂的问题,我觉得很难集中注意力。

    我有一门课:

     public class Location
     {
        public string Name { get; set; }
    
        public List<Location> ChildLocations { get; set; }
     }
    

    它再次引用子位置对象。这是一个递归列表,我想把这个列表输出到一个格式很好的HTML表中。代码如下:

    using System.Collections.Generic;
    using System.Web.Mvc;
    
    namespace WebApplication1.Controllers
    {
        public class Location
        {
            public string Name { get; set; }
    
            public List<Location> ChildLocations { get; set; }
        }
    
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                List<Location> locations = new List<Location>() {
                     new Location() {
                        Name = "Item 1",
                        ChildLocations = new List<Location>() {
                            new Location()
                            {
                                Name="Female",
                                ChildLocations = new List<Location>()
                                {
                                    new Location() { Name = "Female 1" },
                                    new Location() { Name = "Female 2" }
                                }
    
                            },
                            new Location()
                            {
                                Name="Male",
                                ChildLocations = new List<Location>()
                                {
                                    new Location() { Name = "Male 1" },
                                    new Location() { Name = "Male 2" }
                                }
    
                            }
                        }
                    },
                     new Location() {
                        Name = "Item 2",
                        ChildLocations = new List<Location>() {
                            new Location()
                            {
                                Name="Female",
                                ChildLocations = new List<Location>()
                                {
                                    new Location() { Name = "M1" },
                                    new Location() { Name = "M2" },
                                    new Location() { Name = "M3" }
                                }
    
                            },
                            new Location()
                            {
                                Name="Male",
                                ChildLocations = new List<Location>()                            
    
                            },
                            new Location()
                            {
                                Name="Unknown",
                                ChildLocations = new List<Location>()
    
                            }
                        }
                    }
                };
    
                return View(locations);
            }
        }
    }
    

    我想让输出像这样:

    enter image description here

    <table class="table table-border">
      <tr>
        <td rowspan="4">
          Item 1
        </td>
        <td rowspan="2">
          Female
        </td>   
        <td>
          Female 1
        </td>
      </tr> 
      <tr>
        <td>Female 2</td>
      </tr>
      <tr>
        <td rowspan="2">Male</td>
        <td>Male 1</td>
      </tr>
      <tr>
          <td>Male 2</td>    
      </tr>
      <tr>
        <td rowspan="5">
          Item 2      
        </td>
        <td rowspan="3">Female</td>
        <td>M1</td>
      </tr>
      <tr>
        <td>M2</td>
      </tr>
      <tr>
        <td>
          M3
        </td>
      </tr>
      <tr>
        <td>Male</td>
      </tr>
      <tr>
        <td>Unknown</td>
      </tr>
    </table>
    

    如果有人想查看HTML,我有JSFIDLE:

    https://jsfiddle.net/4pk0necp/

    我想使用我在cshtml中按语法提供的数据呈现这种类型的表:

    @using WebApplication1.Controllers;
    @model List<Location>
    @{
        ViewBag.Title = "Home Page";
    }
    

    我知道需要一个函数来计算行跨度,所以我创建了这个小函数,它可以正确返回深度:

    public static int GetDepth(this Location location)
        {
            int noOfchildren = 0;
            bool counted = false;
            foreach (Location item in location.ChildLocations)
            {
                if (item.ChildLocations.Count <= 0)
                {
                    if (!counted)
                    {
                        noOfchildren += location.ChildLocations.Where(i => i.ChildLocations.Count <= 0).Count();
                        counted = true;
                    }
                }
                else
                    noOfchildren += GetDepth(item);
            }
    
            return noOfchildren;
        }
    

    给出的关于树的列表只是一个示例,树中可以有很多层次的深度。非常感谢您的帮助。

    编辑: 我调整了GetDepth函数,因为我们只需要叶级节点计数。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Evan Trimboli    6 年前

    尝试使用以下类。仅勉强测试:

    public class TreeDrawer {
    
        private readonly Dictionary<Location, int> _depthMap;
    
        public TreeDrawer() {
            _depthMap = new Dictionary<Location, int>();
        }
    
        public string Draw(IEnumerable<Location> locations) {
            var sb = new StringBuilder("<table>");
    
            bool first = true;
            foreach (var l in locations) {
                Draw(l, sb, true, first);
                first = false;
            }
    
            sb.Append("</table>");
    
            return sb.ToString();
        }
    
        private void Draw(Location l, StringBuilder sb, bool fromRoot, bool first) {
            int depth = GetDepth(l);
            bool openedRow = false;
            if (fromRoot || !first) {
                sb.Append("<tr>");
                openedRow = true;
            }
            sb.Append("<td");
            if (depth > 1) {
                sb.Append(" rowspan=\"");
                sb.Append(depth);
                sb.Append("\"");
            }
            sb.Append(">");
            sb.Append(l.Name);
            sb.Append("</td>");
    
            bool isFirstChild = true;
            if (l.ChildLocations != null) {
                foreach (var child in l.ChildLocations) {
                    Draw(child, sb, false, isFirstChild);
                    isFirstChild = false;
                }
            }
            if (openedRow) {
                sb.Append("</tr>");
            }
    
        }
    
        private int GetDepth(Location l) {
            if (!_depthMap.ContainsKey(l)) {
                _depthMap.Add(l, Math.Max(1, l.ChildLocations?.Sum(GetDepth) ?? 0));
            }
            return _depthMap[l];
        }
    }