【问题标题】:What is a cleaner or more efficient way of iterating over nested collections of objects什么是迭代嵌套对象集合的更清洁或更有效的方法
【发布时间】:2019-01-11 03:13:07
【问题描述】:

我正在从包含 BOM 级别和它们所属的父级的 SQL 中的 BOM 项(物料清单)的扁平列表构建一个具有嵌套层次结构的对象。基本上,作为较小子部件组合的每个父级都需要将其子部件添加到该对象内的集合中。我相信我们的某些部分可能会迭代多达 10 层,我不喜欢这种模式的发展方式。

这个数据集来自一个运行复杂 CTE 查询的存储过程,我对尝试使用 Entity Framework 使其工作不感兴趣,因为我们有很多数据库混合在一起,我们需要更好的控制通过我们正在编写的 SQL(因此是存储过程)。最终这将以 JSON 格式结束,我将在浏览器中呈现为可折叠树。

这是我希望清理一下的丑陋部分。在我开始迭代各个级别之前,这里发生的事情的要点是:

  1. 建立第一个项目(不完整,但这是主要项目)。
  2. 执行存储过程(带参数)以返回数据表。
  3. 创建我要嵌套的对象的平面列表。
  4. 获取 BOM 上存在的嵌套级别的总数。
  5. 以一种非常丑陋的方式迭代各个级别。
public BOMItemModel GetItemBOM(string item)
        {
            try
            {
                var bom = new BOMItemModel { PLPartNumber = item, BOMLevel = 0 };
                var par = new Hashtable();
                par.Add("@Item", item);
                var dt = db.GetDataTable("[part].[getItemBOM]", par);
                var bomList = new List<BOMItemModel>();
                foreach(DataRow r in dt.Rows)
                {
                    bomList.Add(MapBomItem(r));
                }
                var bomLevels = bomList.Max(x => x.BOMLevel);
                for(var i = 1; i < bomLevels; i++)
                {
                    foreach (var b in bomList.Where(x => x.BOMLevel == i).ToList())
                    {
                        if (i == 1)
                        {
                            bom.SubItems.Add(b);
                        }
                        else
                        {
                            if (i == 2)
                            {
                                var lvl2Items = bom.SubItems;
                                var parent1 = lvl2Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
                                if (parent1 != null) parent1.SubItems.Add(b);
                            }
                            if (i == 3)
                            {
                                var lvl2Items = bom.SubItems;
                                foreach(var lvl2 in lvl2Items)
                                {
                                    var lvl3Items = lvl2.SubItems;
                                    var parent2 = lvl3Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
                                    if (parent2 != null) parent2.SubItems.Add(b);
                                }
                            }
                            if (i == 4)
                            {
                                var lvl2Items = bom.SubItems;
                                foreach (var lvl2 in lvl2Items)
                                {
                                    var lvl3Items = lvl2.SubItems;
                                    foreach (var lvl3 in lvl3Items)
                                    {
                                        var lvl4Items = lvl3.SubItems;
                                        var parent3 = lvl4Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
                                        if (parent3 != null) parent3.SubItems.Add(b);
                                    }
                                }
                            }
                            if (i == 5)
                            {
                                var lvl2Items = bom.SubItems;
                                foreach (var lvl2 in lvl2Items)
                                {
                                    var lvl3Items = lvl2.SubItems;
                                    foreach (var lvl3 in lvl3Items)
                                    {
                                        var lvl4Items = lvl3.SubItems;
                                        foreach (var lvl4 in lvl4Items)
                                        {
                                            var lvl5Items = lvl4.SubItems;
                                            var parent4 = lvl5Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
                                            if (parent4 != null) parent4.SubItems.Add(b);
                                        }
                                    }
                                }
                            }
                            if (i == 6)
                            {
                                var lvl2Items = bom.SubItems;
                                foreach (var lvl2 in lvl2Items)
                                {
                                    var lvl3Items = lvl2.SubItems;
                                    foreach (var lvl3 in lvl3Items)
                                    {
                                        var lvl4Items = lvl3.SubItems;
                                        foreach (var lvl4 in lvl4Items)
                                        {
                                            var lvl5Items = lvl4.SubItems;
                                            foreach (var lvl5 in lvl5Items)
                                            {
                                                var lvl6Items = lvl5.SubItems;
                                                var parent5 = lvl6Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
                                                if (parent5 != null) parent5.SubItems.Add(b);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                    }
                }

                return bom;
            }
            catch (Exception ex)
            {
                Log.Error(ex.Message, ex);
            }
            return null;
        }

这是我的模型或类的样子:

public class BOMItemModel
{
    public BOMItemModel()
    {
        SubItems = new List<BOMItemModel>();
    }
   public string ParentPLNumber { get; set; }
   public string PartNumber { get; set; }
   public string PartListName { get; set; }
   public string ItemNumber { get; set; }
   public string PLPartNumber { get; set; }
   public string PartType { get; set; }
   public string PLManufacturer { get; set; }
   public string PLDescription { get; set; }
   public decimal Qty { get; set; }
   public int BOMLevel { get; set; }
   public List<BOMItemModel> SubItems { get; set; }
}

这个类的最后一个属性是我填充父项的子项的地方,它可以嵌套几个层次。

【问题讨论】:

  • 选择所有由PLPartNumber ASC, BOMLevel ASC 订购的产品。然后循环是直截了当的,你甚至可以使用foreach。您只需将当前的PLPartNumberBOMLevel 与之前的进行比较,并知道您是否有新的BOMItemModel 或只是一个新级别。如果两者相同,请继续填写SubItems-list。
  • 我想我明白你在说什么,这可能会简化一些。我对这段代码的真正问题是我需要直接在循环中引用级别,我希望在整个循环中继续向下移动。感觉有一种更简洁的方法(可能将其中的一些方法转移到另一种方法中?)一直向下移动到底层,而无需为每个级别编写“if 块”。这有意义吗?

标签: c# linq iteration


【解决方案1】:

是的,以下会更好:

for (var i = 1; i <= bomLevels; i++)
                {
                    foreach (var b in bomList.Where(x => x.BOMLevel == i).ToList())
                    {
                        if (i == 1)
                        {
                            bom.SubItems.Add(b);
                        }
                        else
                        {
                            var parent = bomList.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber && b.BOMLevel - 1 == x.BOMLevel);
                            if (parent != null) parent.SubItems.Add(b);
                        }


                    }
                }

另外,当找不到父级时,我会抛出一个错误。如需更通用的方法,请查看我的另一个示例:Converting table in tree

【讨论】:

  • 如果您对答案没问题,请标记为已回答。
  • 这似乎可以解决问题。我还没有完全验证树上的所有节点,但我觉得这是有道理的。乍一看,输出看起来不错。很难将我的头脑围绕在具有多个(且深度)嵌套的父/子关系上,并且您的解决方案使其保持简单。感谢您抽出宝贵时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多