在 List<List<T>> 类型中使用 FindAll

3
假设:
public class MyClass
{
   public int ID {get; set; }
   public string Name {get; set; }
}

and

List<MyClass> classList = //populate with MyClass instances of various IDs

我可以做到

List<MyClass> result = classList.FindAll(class => class.ID == 123);

我可以使用这个方法来获取ID为123的类别列表。这个方法很有效,而且看起来很简洁。

现在,如果我有

List<List<MyClass>> listOfClassLists = //populate with Lists of MyClass instances

我该如何获取一个已过滤的列表,其中列表本身也被过滤了。我尝试了:

List<List<MyClass>> result = listOfClassLists.FindAll
                      (list => list.FindAll(class => class.ID == 123).Count > 0);

它看起来很优雅,但却不起作用。它只包括至少一个类具有ID为123的类列表,但它在该列表中包括所有MyClass实例,而不仅仅是匹配的实例。

最终我不得不做

List<List<MyClass>> result = Results(listOfClassLists, 123);

private List<List<MyClass>> Results(List<List<MyClass>> myListOfLists, int id)
{
   List<List<MyClass>> results = new List<List<MyClass>>();
   foreach (List<MyClass> myClassList in myListOfLists)
   {
      List<MyClass> subList = myClassList.FindAll(myClass => myClass.ID == id);
      if (subList.Count > 0)
         results.Add(subList);
   }
   return results;
}

这种方法可以完成工作,但并不够优雅。我只是在寻找更好的方法,在列表的列表上执行FindAll操作。
Ken

4个回答

5

listOfClasses.SelectMany(x=>x).FindAll( /* yadda */)

抱歉,FindAll是List<T>的一个方法。

这段代码

var result = from x in listOfClasses from y in x where SomeCondition(y) select y;

或者

var result = listOfClasses.SelectMany(x=>x).Where(x=>SomeCondition(x));

“from x yadda…” 的答案有点可行。它会将结果展平(我的代码保留了 List of List 数据类型),但这也许不是什么坏事。谢谢。 - Ken Foster

2
为了保持一个列表的列表,你可以像这个例子一样做:
MyClass a = new MyClass() { ID = 123, Name = "Apple" };
MyClass b = new MyClass() { ID = 456, Name = "Banana" };
MyClass c = new MyClass() { ID = 789, Name = "Cherry" };
MyClass d = new MyClass() { ID = 123, Name = "Alpha" };
MyClass e = new MyClass() { ID = 456, Name = "Bravo" };

List<List<MyClass>> lists = new List<List<MyClass>>()
{
    new List<MyClass>() { a, b, c },
    new List<MyClass>() { d, e },
    new List<MyClass>() { b, c, e}
};

var query = lists
            .Select(list => list.Where(item => item.ID == 123).ToList())
            .Where(list => list.Count > 0).ToList();

需要查询的是一个 List<List<MyClass>>,其中包含通过测试的 MyClass 对象列表。乍一看,Where 扩展出现在 Select 之后似乎有些不对,但内部列表的转换需要先进行,这就是 Select 扩展所做的事情。然后再通过 Where 进行过滤。


这不错。但是我应该在哪里放置.Count > 0检查以删除没有匹配项的List<int>列表? - Ken Foster
好的,发现了。我刚刚在问题中注意到了这一点。我会更新示例以符合您的情况。 - Anthony Pegram
这个很好用。谢谢。我会把所有的想法都交给我的开发者,但我喜欢这个。 - Ken Foster

0

我可能会选择这个

List<List<string>> stuff = new List<List<string>>();

List<List<string>> results = new List<List<string>>();

stuff.ForEach(list=> {var result = list.FindAll(i => i == "fun").ToList();
        if (result.Count > 0) results.Add(result);
    });

List<string> flatResult = new List<string>();

stuff.ForEach(List => flatResult.AddRange(List.FindAll(i => i == "fun")));

那样你可以选择使用嵌套数组或者将其展开。不过Linq的方式也很好用哦 :-).

0

虽然生成一个平面的List<MyClass>通常可以满足您的需求,但对于您的问题,确切的答案是:

var result = (from list in ListOfClassLists
                          let listWithTheId=
                              (
                               (from myClass in list
                                where myClass.ID == id
                                select myClass)
                                .ToList()
                              )
                          where listWithTheId.Count > 0
                          select listWithTheId
             ).ToList();

这段代码片段是从我的概念验证中提取的:

using System.Collections.Generic;
using System.Linq;

namespace ListOfListSelectionSpike
{
    public class ListSpikeClass
    {
        public List<List<MyClass>> ListOfClassLists { get; set; }

        private List<MyClass> list1, list2, list3;

        public ListSpikeClass()
        {
            var myClassWithId123 = new MyClass("123");
            var myClassWithIs345 = new MyClass("456");
            list1 = new List<MyClass> { myClassWithId123, myClassWithIs345 };
            list2 = new List<MyClass> { myClassWithId123, myClassWithIs345, myClassWithId123 };
            list3 = new List<MyClass> { myClassWithIs345, myClassWithIs345 };
            ListOfClassLists = new List<List<MyClass>> { list1, list2, list3 };
        }

        public List<List<MyClass>> GetListOfListsById(string id)
        {
            var result = (from list in ListOfClassLists
                          let listWithTheId =
                              ((from myClass in list
                                where myClass.ID == id
                                select myClass)
                                .ToList())
                          where listWithTheId.Count > 0
                          select listWithTheId)
                          .ToList();
            return result;
        }
    }

    public class MyClass
    {
        public MyClass(string id)
        {
            ID = id;
            Name = "My ID=" + id;
        }
        public string ID { get; set; }
        public string Name { get; set; }
    }
}

这与安东尼的解决方案类似,但使用SQL语法而不是“点”语法,对吧?我看到在stackoverflow上给出问题解决方案时,两种风格都被使用,这只是风格偏好的问题吗?我还没有看到哪种风格具有性能优势或“未来证明”的优势。感谢您的工作,它确实让我受益匪浅。 - Ken Foster
是的,它与安东尼的解决方案类似。通常我发现LINQ符号更易读,但在这种情况下,安东尼的解决方案简洁明了。 - Dabblernl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接