迭代时从对象数组中删除元素 [英] Remove element from an array of objects while iterating

查看:61
本文介绍了迭代时从对象数组中删除元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个要遍历外部扩展程序的ID /父母列表。
该列表可以包含大约11000行,这就是为什么我需要删除一些元素,所以我只显示我需要的元素。

I have a list of id/parents that i'm iterating through an external extension. This list can have around 11 000 lines and that's why i need to remove some elements so i will just display the ones i need.

元素列表:

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"},
};

在迭代过程中,我想删除一些元素,以使其不会被处理或显示,但是我要删除的元素仍然出现在输出中!

While i'm iterating, i want to remove some elements so they will not be processed or displayed, but the element i want to remove still present in the output !

下面是遍历元素的代码:

Here's the code iterating trough the elements:

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上的孩子。
这是我应该在结尾处包含的元素,应该对其进行处理以构建树视图:

I only know the starting position (the id = 4) which is the root parent. I will only know the positions of the elements i want to remove once i have the depth value. The idea is to display only the childrens attached to the id = 4. Here's the elements i should have at the end and that should be processed to built my treeview:

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)));
      }


推荐答案

而不是

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

尝试

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

如果您不知道ID,但知道索引(列表中的偏移量),则可以使用 Where 的替代重载,它将索引作为第二个参数提供给where委托。

If you don't know the ID but know the index (offset into the list), you can get the index using an alternative overload of Where, which supplies the index to the where delegate as a second argument.

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

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

or you could simply do this (which is slightly less efficient):

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

如果(根据您的编辑)您正在寻找孩子和孙辈的列表,给定父母ID,则可以使用像这样的方法来搜索树:

If (based on your edits) you are looking for a list of children and grandchildren, given a parent ID, you can use a method like this one to search the tree:

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);

DotNetFiddle上的示例

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

If you need to limit the levels, you can try this approach, which is less efficient but makes it clear which level each child is at and when to stop the search:

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 );
}

DotNetFiddle上的示例

这篇关于迭代时从对象数组中删除元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆