过滤嵌套列表并作为树输出

本文关键字:输出 嵌套 列表 过滤 | 更新日期: 2023-09-27 18:12:49

我有一个如下的类

public class Material
{
  public string `MaterialName` { get; set; }
  public List<Material> `ChildMaterial` { get; set; }
}

我已经使用上面的类创建了一个嵌套列表,如下面的例子

    材料
  • 材料B
  • 材料C
    • 材料D
  • 材料J
    • 材料D
      • 材料F
  • 2材料

我想要一个linq查询一些方法,这将过滤掉材料D,并会给我的路径,直到根,并将删除它下面的所有节点。根据下面的例子,这个材料D可以在树的任何级别找到,并且可以重复。

  • 物料A
  • 材料B
  • 材料C
  • 材料D
  • 材料J
  • 材料D

过滤嵌套列表并作为树输出

首先,您需要一个LINQ方法,它能够平面化这样的树结构,以便您能够遍历所有可用的节点:

public static IEnumerable<TSource> Map<TSource>(
  this IEnumerable<TSource> source,
  Func<TSource, bool> selectorFunction,
  Func<TSource, IEnumerable<TSource>> getChildrenFunction)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (selectorFunction == null)
        throw new ArgumentNullException("selectorFunction");
    if (getChildrenFunction == null)
        throw new ArgumentNullException("getChildrenFunction");
    return MapImpl(source, selectorFunction, getChildrenFunction);
}
private static IEnumerable<TSource> MapImpl<TSource>(
  IEnumerable<TSource> source,
  Func<TSource, bool> selectorFunction,
  Func<TSource, IEnumerable<TSource>> getChildrenFunction)
{
    // Go through the input enumerable looking for children,
    // and add those if we have them
    foreach (TSource element in source)
    {
        foreach (var childElement in MapImpl(getChildrenFunction(element), selectorFunction, getChildrenFunction))
        {
            yield return childElement;
        }
        if (selectorFunction(element))
            yield return element;
    }
}

现在你可以遍历主列表并找到所有想要的节点:

var matchingMaterials = MyMaterials.Map(material => true, material => material, material => material.ChildMeterials)
                                   .Where(material => material.Name = "Material D")
                                   // Materialization is needed, cause manipulation is desired while iterating over the result.
                                   .ToList();

然后你想以某种方式操作这些节点(例如从匹配节点中删除所有子节点):

foreach(var material in matchingMaterials)
{
    material.ChildMaterials.Clear();
}