Mike Perrenoud
Mike Perrenoud

Reputation: 67898

How can I keep from iterating this collection twice?

NOTE: I've changed the names of the collections because of confidentiality.

Consider the following code:

o.Flag =
    o2.Collection1
        .Any(cpd => cpd.Collection1
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag))) ||
    o2.Collection1
        .Any(cpd => cpd.Collection2
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag)));

Clearly what I'm doing is flattening the detailed flags into an overall flag, but this code as is iterates o2.Collection1 twice.

I don't see how SelectMany can do the job because the flags are on two different collections (i.e. I'm not flattening a collection of collections).

How can I keep from doing that?

NOTE: I feel like Jon Skeet, when I read this post (Using LINQ to flatten a hierarchical dataset - with a caveat) was basically saying I'm stuck. But hopefully I'm reading it wrong!

Upvotes: 0

Views: 128

Answers (2)

Servy
Servy

Reputation: 203827

If the collections are of the same type (or have a common base type defining sufficient information) you can simply concat them in your query:

o.Flag =
    o2.Collection1
        .Any(cpd => cpd.Collection1.Concat(cpd.Collection2)
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag)));

Note that the primary advantage here is simply making a simpler query, and reducing code duplication. It's not going to have any noticeable effect on how well it performs as long as your collection is actually an in memory collection

If they are not of compatible types (or there are subtle differences in the predicates for each sub-collection, unlike the example given), then the best that you can manage is to pull both sub-queries into the predicate of the main collection:

o.Flag =
    o2.Collection1
        .Any(cpd => cpd.Collection1
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag)) ||
            cpd.Collection2
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag)));

Upvotes: 6

user1726343
user1726343

Reputation:

You could use a single iteration up till the first layer, then branch into two separate queries.

o.Flag =
    o2.Collection1
        .Any(cpd => cpd.Collection1
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag)) || 
             cpd.Collection2
            .Any(plc => plc.Collection1
                .Any(vd => vd.DetailedFlag)));

Upvotes: 4

Related Questions