views:

206

answers:

4

Assuming

public class MyClass
{
   public int ID {get; set; }
   public string Name {get; set; }
}

and

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

I can do

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

and that will give me a list of just classes with ID = 123. Works great, looks elegant.

Now, if I had

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

How do I get a filtered list where the lists themselves are filtered. I tried

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

it looks elegant, but doesn't work. It only includes Lists of classes where at least one class has an ID of 123, but it includes ALL MyClass instances in that list, not just the ones that match.

I ended up having to do

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

which gets the job done, but isn't that elegant. Just looking for better ways to do a FindAll on a List of Lists.
Ken

+4  A: 

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

Sorry about that, FindAll is a method of List<T>.

This

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

or

var result = listOfClasses.SelectMany(x=>x).Where(x=>SomeCondition(x));
Will
the 'from x yadda..' answer sort of works. It flattens out the results (mine maintains the List of List data type) but that may not be a bad thing. Thanks.
Ken Foster
A: 

I would probably go with this

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

That way you can go with a jagged array or flatten it out.. But the Linq way works well too :-).

Josh Handel
+1  A: 

To keep a list of lists, you could do something like this example:

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

query would be List<List<MyClass>> holding lists of MyClass objects that passed the test. At first glance, it looks out of order with the Where extension coming after the Select, but the transformation of the inner lists needs to occur first, and that's what's happening in the Select extension. Then it is filtered by the Where.

Anthony Pegram
this isn't bad. But where would I put the .Count > 0 check to remove List<int> lists that have no matching items?
Ken Foster
Good catch, I just noticed that in the question a moment ago. I'll update the example to match your scenario.
Anthony Pegram
this works great. Thanks. I'll toss all the ideas to my developer, but I like this one.
Ken Foster
A: 

While producing a flat List<MyClass> will answer your need most of the time, the exact answer to your question is:

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

This code snippet was taken from my Proof of Concept:

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; }
    }
}
Dabblernl
This is similar to Anthony's solution but using the SQL syntax rather than a 'dot' syntax right? I see both styles used when folks give solutions to questions on stackoverflow, is it just a matter of style preference? I've yet to see where one style has a performance advantage or 'future proof' advantage.Thanks for your work, it certainly gives me more education.
Ken Foster
yes it is similar to Anthony's solution. Usually I find the LINQ notation more readable, but in this case Anthpny's solution is neat and concise.
Dabblernl