【问题标题】:Get items from a tree structure ( N number of nested lists) using LINQ and recursion - c#使用 LINQ 和递归从树结构(N 个嵌套列表)中获取项目 - c#
【发布时间】:2018-11-25 14:28:44
【问题描述】:

我有一个 Class1 类型的对象。 Class1 具有以下属性:member(int)、parent(Class1)、lstMembers(of Class1)。

例如:

对象 A 没有父对象(父对象 == null)。在对象 A 的列表中,我们当然有两个 Class1 类型的对象 A1 和 A2。在对象 A1 的列表中,我们有一个对象 A11。在对象 A2 的列表中,我们有一个对象 A21。

A -> A1, A2
A1 -> A11
A2 -> A21

所以树中有三个层次。层数是已知的。我想使用 LINQ 和递归获取树中每个对象的属性 Member 的值。我似乎无法找到解决方案。请注意,由于某些特殊原因,我无法删除列表中的对象。我确实找到了一个解决方案,其中包括从嵌套列表中删除对象(递归解决方案)。

从列表中删除对象的示例代码:

static void GetMembers(Class1 child, Class1 parent, ref List<int> lstMember)
    {
        // last level
        if (child.lstC.Count == 0)
        {
            lstMember.Add(child.member);
            Console.WriteLine(child.member + " added " + child.name);
            // remove all from last level
            if (parent != null)
            {
                parent.lstC.Remove(child);
                if (parent.lstC.Count != 0)
                    GetMembers(parent.lstC.First(), parent, ref lstMember);
            }
        }
        // group level
        else
        {
            GetMembers(child.lstC.First(), child, ref lstMember);

            if (parent != null && parent.lstC.Count != 0)
            {
                GetMembers(parent.lstC.First(), parent, ref lstMember);
            }
            else
            {
                GetMembers(child, null, ref lstMember);
            }
        }
    }

我想在不使用 LINQ 删除项目的情况下获取项目

【问题讨论】:

    标签: c# linq recursion


    【解决方案1】:

    我为你做了两种扩展方法。
    第一个将搜索以测试是否有一个项目在树中。

    注意:您无法从列表中删除的原因是因为它在被枚举时无法更改。这是收藏的安全性。答案是复制列表或集合。我在我提供的删除代码示例中使用ToList() 完成了这项工作。请注意,我不需要在搜索版本中这样做,因为没有更改。

    您提供搜索方式。 您提供访问嵌套类型的方法。

    这是在树中搜索项目的示例:

    var isInTree = dataList.FoundInTree(searchItem: item => item.Id == 6,
                                        nestedItems: item => item.NestedData);
    

    以下是从树中删除项目的示例:

    var removedFromTree = dataList.RemoveFirstFromTree(searchItem: item => item.Id == 6,
                                                       nestedItems: item => item.NestedData);
    

    我已经包含了这些方法的重载/变体,以允许搜索/删除相同的多个项目:

    var isInTree = dataList.FoundInTree(searchItem: item => item.Id == 6,
                                        nestedItems: item => item.NestedData,
                                        out int foundCount);
    
    var removedFromTree = dataList.RemoveAllFromTree(searchItem: item => item.Id == 6,
                                                     nestedItems: item => item.NestedData,
                                                     out int removedCount);
    

    我还包含了一个示例应用程序,它模拟数据以显示如何使用它,但首先是扩展方法:

    扩展方法(这是您的代码答案)

    public static class Extensions
    {
        public static bool FoundInTree<T>(this IEnumerable<T> items, Func<T, bool> searchItem, Func<T, IEnumerable<T>> nestedItems)
        {
            foreach (var item in items)
            {
                if (searchItem.Invoke(item)) return true;
                if (nestedItems.Invoke(item).FoundInTree(searchItem, nestedItems)) return true;
            }
            return false;
        }
    
        public static bool RemoveFirstFromTree<T>(this ICollection<T> items, Func<T, bool> searchItem, Func<T, ICollection<T>> nestedItems)
        {
            foreach (var item in items.ToList())
            {
                if (searchItem.Invoke(item))
                {
                    items.Remove(item);
                    return true;
                }
                if (nestedItems.Invoke(item).RemoveFirstFromTree(searchItem, nestedItems))
                {
                    return true;
                }
            }
            return false;
        }
    
        public static bool FoundInTree<T>(this IEnumerable<T> items, Func<T, bool> searchItem, Func<T, IEnumerable<T>> nestedItems, out int count)
        {
            count = 0;
            foreach (var item in items)
            {
                if (searchItem.Invoke(item)) count++;
                if (nestedItems.Invoke(item).FoundInTree(searchItem, nestedItems, out int nestedCount)) count += nestedCount;
            }
            return count > 0;
        }
    
        public static bool RemoveAllFromTree<T>(this ICollection<T> items, Func<T, bool> searchItem, Func<T, ICollection<T>> nestedItems, out int count)
        {
            var isAnyRemoved = false;
            count = 0;
            foreach (var item in items.ToList())
            {
                if (searchItem.Invoke(item))
                {
                    items.Remove(item);
                    isAnyRemoved = true;
                    count++;
                }
                if (nestedItems.Invoke(item).RemoveAllFromTree(searchItem, nestedItems, out int nestedCount))
                {
                    isAnyRemoved = true;
                    count += nestedCount;
                }
            }
            return isAnyRemoved;
        }
    }
    

    现在这是完整的控制台应用程序(包括扩展方法)。您可以按原样复制和粘贴它,然后随意使用它。

    控制台应用

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Data;
    using System.Linq;
    using System.Net.Http.Headers;
    using System.Text;
    namespace Question_Answer_Console_App
    {
        class Program
        {
            static void Main(string[] args)
            {
                var dataList = GetMockData();
    
                var isInTree = dataList.FoundInTree(searchItem: item => item.Id == 6,
                                                    nestedItems: item => item.NestedData,
                                                    out int foundCount);
    
                if (isInTree)
                {
                    Console.WriteLine($"Found {foundCount} items in the tree.");
                }
                else
                {
                    Console.WriteLine("None found in the tree.");
                }
    
                var removedFromTree = dataList.RemoveAllFromTree(searchItem: item => item.Id == 6,
                                                                 nestedItems: item => item.NestedData,
                                                                 out int removedCount);
    
                if (removedFromTree)
                {
                    Console.WriteLine($"Removed {removedCount} items from the tree.");
                }
                else
                {
                    Console.WriteLine($"No items removed from the tree.");
                }
    
                isInTree = dataList.FoundInTree(searchItem: item => item.Id == 6,
                                               nestedItems: (item) => item.NestedData);
    
                if (isInTree)
                {
                    Console.WriteLine($"Found {foundCount} items in the tree.");
                }
                else
                {
                    Console.WriteLine("None found in the tree.");
                }
    
    
                Console.ReadKey();
            }
    
            private static List<Data<int>> GetMockData()
            {
                var dataList = new List<Data<int>>();
    
                for (var i = 0; i < 7; i++)
                {
                    dataList.Add(getData(i));
                }
    
                return dataList;
    
                Data<int> getData(int count)
                {
                    var data = new Data<int>
                    {
                        Id = count
                    };
    
                    for (var j = count; j > 0; j--)
                    {
                        var innerData = new Data<int>
                        {
                            Id = j
                        };
    
                        innerData.NestedData.Add(getData(--j));
                        data.NestedData.Add(innerData);
                    }
    
                    return data;
                }
            }
        }
    
        public class Data<T>
        {
            public T Id { get; set; }
            public List<Data<T>> NestedData { get; set; } = new List<Data<T>>();
        }
    
        public static class Extensions
        {
            public static bool FoundInTree<T>(this IEnumerable<T> items, Func<T, bool> searchItem, Func<T, IEnumerable<T>> nestedItems)
            {
                foreach (var item in items)
                {
                    if (searchItem.Invoke(item)) return true;
                    if (nestedItems.Invoke(item).FoundInTree(searchItem, nestedItems)) return true;
                }
                return false;
            }
    
            public static bool RemoveFirstFromTree<T>(this ICollection<T> items, Func<T, bool> searchItem, Func<T, ICollection<T>> nestedItems)
            {
                foreach (var item in items.ToList())
                {
                    if (searchItem.Invoke(item))
                    {
                        items.Remove(item);
                        return true;
                    }
                    if (nestedItems.Invoke(item).RemoveFirstFromTree(searchItem, nestedItems))
                    {
                        return true;
                    }
                }
                return false;
            }
    
            public static bool FoundInTree<T>(this IEnumerable<T> items, Func<T, bool> searchItem, Func<T, IEnumerable<T>> nestedItems, out int count)
            {
                count = 0;
                foreach (var item in items)
                {
                    if (searchItem.Invoke(item)) count++;
                    if (nestedItems.Invoke(item).FoundInTree(searchItem, nestedItems, out int nestedCount)) count += nestedCount;
                }
                return count > 0;
            }
    
            public static bool RemoveAllFromTree<T>(this ICollection<T> items, Func<T, bool> searchItem, Func<T, ICollection<T>> nestedItems, out int count)
            {
                var isAnyRemoved = false;
                count = 0;
                foreach (var item in items.ToList())
                {
                    if (searchItem.Invoke(item))
                    {
                        items.Remove(item);
                        isAnyRemoved = true;
                        count++;
                    }
                    if (nestedItems.Invoke(item).RemoveAllFromTree(searchItem, nestedItems, out int nestedCount))
                    {
                        isAnyRemoved = true;
                        count += nestedCount;
                    }
                }
                return isAnyRemoved;
            }
        }
    }
    
    //OUTPUTS
    //Found 2 items in the tree.
    //Removed 2 items from the tree.
    //None found in the tree.
    

    【讨论】:

      【解决方案2】:

      这就是我最终得到的结果,希望在正确的抽象级别

      void Main()
      {
          var dataStructure = Init_DataStructure();
      
          List<int> members = GetMembers(dataStructure).ToList();
      }
      
      IEnumerable<int> GetMembers(Class1 input)
      {
          yield return input.Member;
      
          foreach (var subMember in input.MemberList.SelectMany(c => GetMembers(c)))
              yield return subMember;
      }
      
      Class1 Init_DataStructure()
      {
          Class1
              a1 = new Class1
              {
                  Id = "A1",
                  Member = 1,
                  MemberList = new Collection<Class1>()
              },
              a11 = new Class1
              {
                  Id = "A11",
                  Member = 2,
                  Parent = a1
              };
          a1.MemberList.Add(a11);
      
          Class1
              a2 = new Class1
              {
                  Id = "A2",
                  Member = 3,
                  MemberList = new Collection<Class1>()
              },
              a21 = new Class1
              {
                  Id = "A21",
                  Member = 4,
                  Parent = a2
              };
          a2.MemberList.Add(a21);
      
          return new Class1
          {
              Id = "A",
              Member = 0,
              MemberList = new Collection<Class1> { a1, a2 }
          };
      }
      
      class Class1
      {
          public Class1() => MemberList = new Collection<Class1>();
      
          public Class1 Parent { get; set; }
      
          public string Id { get; set; }
          public int Member { get; set; }
      
          public ICollection<Class1> MemberList { get; set; }
      }
      

      【讨论】:

        【解决方案3】:

        你在这里:

        class Class1
        {
            public Class1(string Name)
            {
                lstC = new List<Class1>();
                this.name = Name;
            }
            public List<Class1> lstC { get; set; }
        
            public string name { get; }
            public int member { get; set; }
        }
        
        class Program
        {
            static void Main(string[] args)
            {
                Class1 par = new Class1("A") { member = 4 };
                Class1 c1 = new Class1("A1") { member = 54};
                Class1 c2 = new Class1("A2") { member = 5 };
                par.lstC.Add(c1);
                par.lstC.Add(c2);
                Class1 c11 = new Class1("A11") { member = 39 };
                c1.lstC.Add(c11);
                Class1 c21 = new Class1("A21") { member = 67 };
                c2.lstC.Add(c21);
        
                List<int> result = new List<int>();
        
                GetMembers(par, ref result);
            }
        
            static void GetMembers(Class1 parent, ref List<int> lstMember)
            {
                lstMember.Add(parent.member);
        
                foreach (var child in parent.lstC)
                {
                    GetMembers(child, ref lstMember);
                }       
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2015-08-04
          • 2018-09-12
          • 1970-01-01
          • 2022-12-11
          • 1970-01-01
          • 2017-08-25
          • 2020-01-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多