Val
Val

Reputation: 2323

LINQ query to search multiple conditions in a specific order?

I have a static set of categories, and an incoming list of items that are in various categories. I want to get the first item that matches the best category, and if none is found, get the first item that matches the next-best category, etc. until I get to a default category.

I tried putting my categories in an IEnumerable and doing a Contains() on it to see if my incoming items match a category, and stopping on the first match. But I can't control the match order: if the category list is ordered by priority [best, OK, default], the first input item to match anything in the category list wins. So if my first input item matches OK, and the second input item matches Best, then I'll stop before I get the Best match.

Right now I run Where() over the item list, and if the result is null I run a second Where(), etc. I'd like to know if there's a more concise (or more LINQ-idiomatic) way to write this:

    public MyVM getVM( IEnumerable<Entities.Foo> foos )
    {
        Entities.Foo foo  = null;
        MyVM         myVM = null;

        if ( entity.IsBar ) 
        {
            foo = foos.Where( f => f.FooCatId == FooCategories.BestCat ).FirstOrDefault();
        }

        // no foos are BestCat, look for OkCat
        if ( foo == null ) 
        {
            foo = foos.Where( f => f.FooCatId == FooCategories.OkCat ).FirstOrDefault();
        }

        // no foos are OkCat, look for DefaultCat
        if ( foo == null ) 
        {
            foo = foos.Where( f => f.FooCatId == FooCategories.DefaultCat ).FirstOrDefault();
        }

        if ( foo != null ) 
        {
            myVM = new MyVM() { Name = foo.Name };
        }
        return myVM;
    }

    public enum FooCategories
    {
        DefaultCat,
        SomeCat,
        AnotherCat,
        OkCat,
        BestCat,
        BadCat
    }

Upvotes: 1

Views: 1465

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500245

There's certainly a more concise way of doing it, in two ways:

  • Use the overload of FirstOrDefault which takes a predicate
  • Use the null-coalescing operator

I'm going to ignore your IsBar check for now, because I don't understand how that fits in... but the rest would be:

var foo = foos.FirstOrDefault(f => f.FooCatId == FooCategories.BestCat)
       ?? foos.FirstOrDefault(f => f.FooCatId == FooCategories.OkCat)
       ?? foos.FirstOrDefault(f => f.FooCatId == FooCategories.DefaultCat);

Another option would be to change your enum order so that you could just find the cat with the best category - either by using OrderByDescending(f => f.FooCatId) or by using MaxBy from MoreLINQ. You'd also then need to check that the resulting cat isn't a bad cat, etc, so it might not be much of a win - but MaxBy would at least be more efficient, by only going through the list once.

Upvotes: 4

Related Questions