迭代时从对象数组中删除元素

时间:2018-06-14 18:52:07

标签: c# arrays

我有一个id / parents列表,我正在迭代外部扩展。 这个列表可以有大约11 000行,这就是为什么我需要删除一些元素,所以我只显示我需要的。

元素列表:

FlatData[] elements = new FlatData[]
{
   new FlatData {Id = 3, ParentId = 1, Text = "A"}, 
   new FlatData {Id = 4, ParentId = 1, Text = "D"},
   new FlatData {Id = 5, ParentId = 2, Text = "E"},
   new FlatData {Id = 7, ParentId = 2, Text = "G"},
   new FlatData {Id = 8, ParentId = 4, Text = "H"},
   new FlatData {Id = 9, ParentId = 8, Text = "H"},
   new FlatData {Id = 10, ParentId = 3, Text = "I"},
   new FlatData {Id = 11, Text = "I"},
};

虽然我正在迭代,但我想删除一些元素,以便它们不会被处理或显示,但我要删除的元素仍然存在于输出中!

这里的代码通过元素迭代:

int firstDepth = 0;

IEnumerable <DeepNodeData> nodes = elements.Where(x => x.Id >= 5).RecursiveJoin(element => element.Id,
     element => element.ParentId,
    (FlatData element, int index, int depth, IEnumerable<DeepNodeData> children) =>
     {
          int  position;

          if(depth == 0){
            firstDepth++;
          }

          if(firstDepth > 0){
              position= Array.FindIndex(elements, row => row.Id == index);
              elements.Skip(position);

             // or much better, exit the program with something like break ?;
          }

          return new DeepNodeData()
          {
              Id = element.Id,
              Index = index,
              Text = element.Text,
              Children = children
          };
    });

我只知道作为根父母的起始位置(id = 4)。一旦我有了深度值,我只会知道要删除的元素的位置。这个想法是只显示附加到id = 4的子项。 这是我最后应该拥有的元素,应该处理以构建我的树视图:

FlatData[] elements = new FlatData[]
 {
     new FlatData {Id = 4, ParentId = 1, Text = "D" },
     new FlatData {Id = 8, ParentId = 4, Text = "H" },
     new FlatData {Id = 9, ParentId = 8, Text = "H" },
 };

最终输出应为:

    [
    {
    "id": 4,
    "index": 1,
    "depth": 0,
    "parentId": 0,
    "text": "D",
    "children": [
       {
       "id": 8,
       "index": 1,
       "depth": 1,
       "parentId": 0,
       "text": "H",
       "children": [
          {
           "id": 9,
           "index": 1,
           "depth": 2,
           "parentId": 0,
            "text": "H",
            "children": []
          }
        ]
      }
    ]
   }
  ]

递归扩展程序:

public static IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this 

    IEnumerable<TSource> source,
         Func<TSource, TKey> parentKeySelector,
         Func<TSource, TKey> childKeySelector,
         Func<TSource, int, int, IEnumerable<TResult>, TResult> resultSelector,
         IComparer<TKey> comparer)
      {
        // prevent source being enumerated more than once per RecursiveJoin call
        source = new LinkedList<TSource>(source);

        // fast binary search lookup
        SortedDictionary<TKey, TSource> parents = new SortedDictionary<TKey, TSource>(comparer);
        SortedDictionary<TKey, LinkedList<TSource>> children
           = new SortedDictionary<TKey, LinkedList<TSource>>(comparer);

        foreach (TSource element in source)
        {
          parents[parentKeySelector(element)] = element;

          LinkedList<TSource> list;

          TKey childKey = childKeySelector(element);

          if (!children.TryGetValue(childKey, out list))
          {
            children[childKey] = list = new LinkedList<TSource>();
          }

          list.AddLast(element);
        }

        // initialize to null otherwise compiler complains at single line assignment
        Func<TSource, int, IEnumerable<TResult>> childSelector = null;

        childSelector = (TSource parent, int depth) =>
        {
          LinkedList<TSource> innerChildren = null;

          if (children.TryGetValue(parentKeySelector(parent), out innerChildren))
          {
            return innerChildren.Select((child, index)
               => resultSelector(child, index, depth , childSelector(child, depth + 1)));
          }
          return Enumerable.Empty<TResult>();
        };

        return source.Where(element => !parents.ContainsKey(childKeySelector(element)))
           .Select((element, index) => resultSelector(element, index, 0 ,childSelector(element, 1)));
      }

1 个答案:

答案 0 :(得分:3)

而不是

.Where(x => x.Id >= 5)

.Where(x => x.Id >= 5 && x.Id != 11)

如果您不知道ID但知道索引(列表中的偏移量),则可以使用alternative overload of Where获取索引,该索引将where索引作为第二个参数提供给where。

    .Where
    (
        (row, index) => row.Id >  5  //Filter on data
                     && index  != 11 //Filter on row index
    )

或者您可以简单地执行此操作(效率稍低):

.Where(x => x.Id >= 5 && x.Id != elements[11].Id)

如果(基于您的编辑)您正在寻找一个包含父ID的子孙列表,您可以使用这样的方法来搜索树:

public static class ExtensionMethods
{
    public static IEnumerable<FlatData> GetDescendents(this IEnumerable<FlatData> This, int rootId)
    {
        var rootItem = This.Single( x => x.Id == rootId );
        var queue = new Queue<FlatData>( new [] { rootItem } );
        while (queue.Count > 0)
        {
            var item = queue.Dequeue();
            yield return item;
            foreach (var child in This.Where( x => x.ParentId == item.Id )) 
            {
                queue.Enqueue(child);
            }
        }
    }
}

然后使用

var filtered = elements.GetDescendents(4);

Example on DotNetFiddle

如果您需要限制级别,您可以尝试这种方法,效率较低,但会明确每个孩子在哪个级别以及何时停止搜索:

public static IEnumerable<FlatData> GetDescendents(this IEnumerable<FlatData> This, int rootId, int maxDepth)
{
    var results = Enumerable.Range(0, maxDepth+1 ).Select( i => new List<FlatData>() ).ToList();
    results[0].Add
    (
        This.Single( x => x.Id == rootId ) 
    );
    for (int level = 1; level <= maxDepth; level++)
    {
        results[level].AddRange
        (
            results[level-1].SelectMany
            ( 
                x => This.Where( y => y.ParentId == x.Id )
            )
        );
    }
    return results.SelectMany( x => x );
}

Example on DotNetFiddle