Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Query multiple lists in one go

Tags:

c#

linq

I have a series of lists:

List<foo> spamSpamAndSpam;
List<foo> spamSpamSpamSausageEggsAndSpam;
List<foo> spamSpamSpamSausageEggsBaconAndSpam;
List<foo> spamSpamSpamSpamSpamWithoutSpam;
List<foo> spamSpamSpamSpamSpamSpamSpamSpamSpamLovelySpamWonderfulSpam;

I also have a series of values I'm looking for in a property of foo:

List<string> Brian= new List<string>();
Brian.Add("Always");
Brian.Add("Look");
Brian.Add("On");
Brian.Add("The");
Brian.Add("Bright");
Brian.Add("Side");
Brian.Add("Of");
Brian.Add("Life");

I'm catching the references to all the foo'ses in which a a given property (say, bar) has a value contained in latter list. I'm doing it like this:

func<foo, bool> deadParrot = x => Brian.Contains(x.bar);

IEnumerable<foo> okLumberjacks = spamSpamAndSpam.Where(deadParrot);
okLumberjacks = okLumberjacks.Concat(
    spamSpamSpamSpamSausageEggsAndSpam.Where(deadParrot));

// And so on, concatenating the results of Where() from every list of <foo>.

I need every foo that has matched the filtering function in a single IEnumerable<foo> so that I can call a method in all of them in one go, like this:

foreach (foo incontinentRunner in okLumberjacks)
{
    incontinentRunner.SillyWalk();
}

So my question is... Is there a way to gather every foo in a single collection without having to resort to something like:

ni = ni.Concat(someList.Where(filter));

I mean, is there some more elegant way to do something like this with Linq? I'm looking for something like:

okLumberjacks = spamSpamAndSpam.
    And(spamSpamSpamSausageEggsAndSpam).
    And(spamSpamSpamSausageEggsBaconAndSpam) /* etc */ .Where(deadParrot);

Or even better:

okLumberjacks = spanishInquisition.Where(deadParrot);
// Where the type of spanishInquisition is List<List<foo>>.

I don't want to modify the original lists of foo because I need them as they are for another operation later on in the code.

like image 699
Geeky Guy Avatar asked Jun 25 '13 21:06

Geeky Guy


3 Answers

Create a list of lists, and then just flatten it:

List<List<foo>> lists = new List<List<foo>>()
{
    spamSpamAndSpam,
    spamSpamSpamSausageEggsAndSpam,
    //etc.
};

IEnumerable<foo> items = lists.SelectMany(item => item);
//do stuff with items.
like image 52
Servy Avatar answered Oct 09 '22 07:10

Servy


Best I can think of would be something like:

var everything = spam1.Concat(spam2).Concat(spam3);
var itemsIWant = everything.Where(x => Brian.Contains(x));
like image 44
Jim Mischel Avatar answered Oct 09 '22 07:10

Jim Mischel


Does Union() resolve your problem?

        List<foo> spamSpamAndSpam;
        List<foo> spamSpamSpamSausageEggsAndSpam;
        List<foo> spamSpamSpamSausageEggsBaconAndSpam;
        List<foo> spamSpamSpamSpamSpamWithoutSpam;
        List<foo> spamSpamSpamSpamSpamSpamSpamSpamSpamLovelySpamWonderfulSpam;

        var x = spamSpamAndSpam
            .Union(spamSpamSpamSausageEggsAndSpam)
            .Union(spamSpamSpamSausageEggsBaconAndSpam)
            .Union(spamSpamSpamSausageEggsBaconAndSpam)
            .Union(spamSpamSpamSpamSpamWithoutSpam)
            .Union(spamSpamSpamSpamSpamSpamSpamSpamSpamLovelySpamWonderfulSpam)
            .Where(x => ..... );

NOTE: as it was properly noted, Union excludes duplicates from the return set. This is different behavior to the Concat method, which returns all the elements in the input sequences including duplicates. Since duplicate exclusion was not a requirement, Concat is probably better

like image 29
galets Avatar answered Oct 09 '22 07:10

galets