开发者

How to perform 2 checks in LINQ Where

public List<SavedOption> GetValidSavedOptions(
    List<Option> itemOptions, 
    List<SavedOption> savedOptions)
{
    List<SavedOption> finalSavedOptions = savedOptions.Where(x => 
        OptionTextDoesMatch(y, x) && 
        itemOptions.Any(y => y.SomeID == x.SomeID)
    ).ToList(); 
}

I am totally new to LINQ and Lambdas.

In the above, what I need/want to do is include the SavedOption only if the call to OptionTextDoesMatch AND SomeID of the savedOption is found in the list of SomeID in itemOptions. If the call to OptionTextDoesMatch returns true AND the current savedOptions SavedOption.SomeID is found in the itemOption collection then it will be in the ToList()

UPDATED:

I tried this but syntax still not working for me:

savedOptions.Where(itemOptions.Any( OptionTextDoesMatch(x,y) && (y => y.SomeID == x.Som开发者_开发百科eID))).ToList();

Now I don't know if I can just throw in x like that. I assume if I do it's going to represent the currrent savedOption and I don't need the => ?


Though the answers above are correct, they are "give a man a fish" answers. It would be better to take this opportunity to learn how to break down a problem into small pieces, and then reassemble the results into queries.

In the above, what I need/want to do is include the SavedOption only if the call to OptionTextDoesMatch AND SomeID of the savedOption is found in the list of SomeID in itemOptions.

Let me try to rephrase this someone confusing sentence.

You have a list of SavedOptions. Each SavedOption has an ID and some text.

You have a list of Options. Each Option has an ID and some text.

You wish to filter the list of SavedOptions in order to get the SavedOptions which match some Option on BOTH text and ID.

Break the problem down. Suppose you did not have a sequence of SavedOptions. Suppose you just had one SavedOption, and a list of Options. How would you tell if it was a match?

That's straightforward:

SavedOption mySavedOption = whatever;
bool matchExists = itemOptions.Any(item=>
    OptionTextDoesMatch(item, mySavedOption) && 
    item.SomeID == mySavedOption.SomeID);

Does that make sense?

Now suppose you wanted to make a predicate out of that which takes a SavedOption. How would you do it? That's straightforward:

Func<SavedOption, bool> predicate = savedOption => 
    itemOptions.Any(item=>
        OptionTextDoesMatch(item, savedOption ) && 
        item.SomeID == savedOption.SomeID);

This is a predicate which determines whether a single item matches.

Still making sense? Stop me if something seems confusing.

So to make a filter out of it, apply the predicate to every item on the list of saved options:

result = savedOptions.Where(savedOption => 
    itemOptions.Any(item=>
        OptionTextDoesMatch(item, savedOption) && 
        item.SomeID == savedOption.SomeID));

Or, in query comprehension form, which I personally find easier to read.

result = from savedOption in savedOptions
         where itemOptions.Any(item =>
             OptionTextDoesMatch(item, savedOption) && 
             item.SomeID == savedOption.SomeID)
         select savedOption;

But a possibly better choice is to use a join. A join is simply an optimization of a filter on the Cartesian product of two collections that are related by an id.

result = from savedOption in savedOptions
         join itemOption in itemOptions 
         on savedOption.SomeID equals itemOption.SomeID
         where OptionTextDoesMatch(itemOption, savedOption)
         select savedOption;

Is that clear?


Random guess:

List<SavedOption> finalSavedOptions = savedOptions.Where(x => 
    itemOptions.Any(y => OptionTextDoesMatch(y, x) && y.SomeID == x.SomeID)
).ToList(); 


In your Where clause you are iterating each object in your savedOptions method - in a similar way as if you did:

foreach(SavedOption x in savedOptions)
{
    if (OptionTextDoesMatch(y, x)) //y is not yet specified...
    {
        foreach(Option y in itemOptions)
        {
            if (y.SomeID == x.SomeID)
                yield return x;
        }
    }
}

In your statement, the where clause iterates your savedOptions list and for each iteration assigns the current instance of savedOption to 'x'. You are then checking that 'x's text matches something you haven't specified yet - 'y'.

You're then doing a second iteration: itemOptions.Any(y => y.SomeID == x.SomeID). Here, you've specified that y is now defined as an instance of Option in much the same way you did with the outer lambda expression:

foreach(Option y in itemOptions)
{
    return y.SomeID == x.SomeID;
}

Because x is specified by the outer clause, we've got access to it in the inner clause, but the opposite isn't true. y is not specified until the inner clause, so consequently your where clause doesn't work.

In order to give a full diagnosis of what you're trying to do, I'd need to understand what the Option and SavedOptions objects look like and figure out what you're logically trying to do in order to explain exactly how your lambda should look...

I suspect what you're really trying to do is something like:

foreach(SavedOption x in savedOptions)
    foreach(Option y in itemOptions)
        if (OptionTextDoesMatch(y, x) && (y.SomeID == x.SomeID))
            yield return x;

Which would in lambda notation would be:

return savedOptions.Where(x => itemOptions.Any(y => OptionTextDoesMatch(y, x) && (y.SomeID == x.SomeID)));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜