使用LINQ和递归从树结构(N个嵌套列表)获取项目-C#

时间:2018-11-25 14:28:45

标签: c# linq recursion

我有一个Class1类型的对象。 Class1具有以下属性:member(int),parent(Class1),lstMembers(属于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删除商品

3 个答案:

答案 0 :(得分:0)

我为您制作了两种扩展方法。
首先搜索要测试的是树中是否有项目。

注意:之所以不能从列表中删除,是因为在枚举时不能对其进行更改。这是集合的安全。答案是复制列表或集合。我已经在提供的删除代码示例中使用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.

答案 1 :(得分:0)

您在这里:

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

答案 2 :(得分:0)

这就是我最后的结果,希望能在正确的抽象水平上

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