LINQ - 在嵌套集合对象上选择语句

时间:2017-05-25 11:48:41

标签: c# linq

我有一个类文件,如下所述:

public class TestA
{
    public string Name {get; set;}

    public List<TestA> TestCollection {get;set;}
}
上面的

您可以看到TestCollection的类型是父类的列表。

现在我想从每个对象中检索名称。

我尝试使用SelectMany,如下所示:但这对我不起作用。

testAList.SelectMany(t => t.Name)

我怎样才能做到这一点?

以下是小提琴代码:Fiddle Code

3 个答案:

答案 0 :(得分:3)

假设testAListList<TestA>,并且您希望每个Name TestA属性中的TestCollection个对象的TestAtestAList内的对象:

testAList.SelectMany(x => x.TestCollection.Select(y => y.Name))

或者,如果您想要Name中的testAList个对象:

testAList.Select(x => x.Name)

最后,如果你想要所有的名字,你需要使用的不仅仅是LINQ,因为你的类的递归性质:

IEnumerable<string> GetNames(List<TestA> testAList)
{
    return testAList.Select(x => x.Name)
                    .Concat(testAList.SelectMany(x => GetNames(x.TestCollection)));
}

var names = GetNames(testAList);

答案 1 :(得分:2)

您的集合是递归的,因此您需要递归查询才能获得结果:

IEnumerable<string> GetAllNames(TestA root) {
    return GetNames(new[] {root}); // Forward to a method taking IEnumerable<T>
}
IEnumerable<string> GetAllNames(IEnumerable<TestA> tests) {
    return tests
        .Select(t => t.Name) // Names at this level
        .Concat(tests.SelectMany(t => GetAllNames(t.TestCollection))); // Names of children
}

但是,C#提供了一种使用收益率回报的更好方法:

IEnumerable<string> GetAllNames(TestA node) {
    yield return node.Name;
    foreach (var childName in node.TestCollection.SelectMany(t => GetAllNames(t))) {
        yield return childName;
    }
}

答案 2 :(得分:0)

这是一个替代选项,它不使用linq但可以提供相同的结果

IEnumerable<string> GetAllNames(TestA root) {
    return GetAllNames(new[] { root });
}

IEnumerable<string> GetAllNames(IEnumerable<TestA> tests) {
    var queue = new Queue<TestA>(tests);
    while (queue.Count > 0) {
        var current = queue.Dequeue();
        yield return current.Name;
        if (current.TestCollection != null) {
            foreach (var child in current.TestCollection) {
                queue.Enqueue(child);
            }
        }
    }
}

以上基于你的小提琴会产生

Parent_0
Parent_1
Parent_2
Parent_3
Parent_4
Child_0_0
Child_0_1
Child_1_0
Child_1_1
Child_2_0
Child_2_1
Child_3_0
Child_3_1
Child_4_0
Child_4_1

如果使用堆叠方法

IEnumerable<string> GetAllNamesStack(IEnumerable<TestA> tests) {
    var stack = new Stack<TestA>(tests.Reverse());
    while (stack.Count > 0) {
        var current = stack.Pop();
        yield return current.Name;
        if (current.TestCollection != null) {
            current.TestCollection.Reverse();
            foreach (var child in current.TestCollection) {
                stack.Push(child);
            }
        }
    }
}

输出为

Parent_0
Child_0_0
Child_0_1
Parent_1
Child_1_0
Child_1_1
Parent_2
Child_2_0
Child_2_1
Parent_3
Child_3_0
Child_3_1
Parent_4
Child_4_0
Child_4_1