【问题标题】:Using Filter expression in C#在 C# 中使用过滤器表达式
【发布时间】:2012-11-24 01:10:14
【问题描述】:

从下面的代码中,我想过滤并希望只得到MasterproductID = 1 及其下的子代。通过过滤MasterproductID = 2MasterproductID = 3。 我可以知道该怎么做吗?感谢您的帮助。

class TreeNode
{
    public int MasterProductId;
    public int? ParentId;
}

protected void Page_Load(object sender, EventArgs e)
{
    var list = new List<TreeNode>{

        new TreeNode{ MasterProductId = 1 },
        new TreeNode{ MasterProductId = 4, ParentId = 1 },
        new TreeNode{ MasterProductId = 7, ParentId= 4 },
        new TreeNode{ MasterProductId = 5, ParentId = 1 },
        new TreeNode{ MasterProductId = 6, ParentId = 1 },

        new TreeNode{ MasterProductId = 2 },
        new TreeNode{ MasterProductId = 7, ParentId = 2 },
        new TreeNode{ MasterProductId = 8, ParentId= 7 },

        new TreeNode{ MasterProductId = 3 },

    };

    foreach (var item in Level(list, null, 0))
    {
        if (item.Value == 0)
        {
            Response.Write(String.Format("<b>MasterProductId={0}, Level={1}", item.Key, item.Value) + "</b><br />");
        }
        else
        {
            Response.Write(String.Format("MasterProductId={0}, Level={1}", item.Key, item.Value) + "<br />");
        }
    }
}

private static IEnumerable<KeyValuePair<int, int>> Level(List<TreeNode> list, int? parentId, int lvl)
{
    return list
        .Where(x => x.ParentId == parentId)
        .SelectMany(x =>
            new[] { new KeyValuePair<int, int>(x.MasterProductId, lvl) }.Concat(Level(list, x.MasterProductId, lvl + 1))
        );
}

【问题讨论】:

    标签: c# filtering


    【解决方案1】:

    Level 方法已经几乎完全符合您的要求。如果foreach被修改为指定ParentId为1:

    foreach (var item in Level(list, 1, 0))
    

    然后它将为节点4、7、8、5和6产生输出。即返回一个节点的后代

    如果你还想包含指定的父节点,你只需要将它连接到结果列表中。例如:

    private static IEnumerable<KeyValuePair<int, int>> LevelIncl(
                     List<TreeNode> list, int parentId)
    {
        return new[] {new KeyValuePair<int, int>(parentId, 0)}
                  .Concat(Level(list, parentId, 1));
    }
    

    然后:

    foreach (var item in LevelIncl(list, 1))
    

    这将为节点 1、4、7、8、5 和 6 生成输出。

    【讨论】:

      【解决方案2】:

      是否有某些原因(从您的示例代码或解释中不明显)您不能简单地使用它?

      .Where(x => x.MasterProductId == 1 || (x.ParentId.HasValue && x.ParentId.Value == 1))
      

      如果按照建议,您正在寻找递归祖先关系,而不仅仅是父关系,那么对您的数据结构进行一些更新将非常有助于实现这一目标。

      class TreeNode
      {
          public int MasterProductId;
          public int? ParentId;
      
          private TreeNode parent; 
              // How this gets set requires other data structures, and some knowledge of
              // how you are loading the data--but this should give you the outline of an idea
      
          public bool HasAncestor(int referenceId, int ancestor) {
      
              if (!parentId.HasValue) 
              { 
                  return false 
              };
      
              if (parentId.Value == ancestor) 
              {
                  return true;
              }
      
              return parent.HasAncestor(ancestor);                
          }
      }
      

      然后你就可以做到:

       .Where(x => x.MasterProductId == 1 || x.HasAncestor(1))
      

      TreeNode 中实现父attribute 的最佳方式取决于您的其余实现细节——我会将其封装在一个容器类中,该类具有Map&lt;int, TreeNode&gt; 以跟踪所有已知节点。该映射将用于构造 TreeNode 以设置父引用。

      【讨论】:

      • 也许查询需要递归?
      • 如果是这样的话,当前的数据模型设计得非常糟糕,因此确实需要进行一些重组以使其相当高效和优雅。在我看来,在内存表示中添加对父级的真实引用(即使这些东西来自例如 SQL 表)将是最好的方法。
      猜你喜欢
      • 2012-07-25
      • 2016-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      • 1970-01-01
      相关资源
      最近更新 更多