How to use LINQ Contains() to find a list of enums?

16,052

Solution 1

Sure:

var status in Order.Status.Where(status => new [] {
        OrderStatus.Valid, 
        OrderStatus.Active, 
        OrderStatus.Processed,
        OrderStatus.Completed
    }.Contains(status.OrderStatus));

You could also define an extension method In() that would accept an object and a params array, and basically wraps the Contains function:

public static bool In<T>(this T theObject, params T[] collection)
{
    return collection.Contains(theObject);
}

This allows you to specify the condition in a more SQL-ish way:

var status in Order.Status.Where(status => 
    status.OrderCode.In(
        OrderStatus.Valid, 
        OrderStatus.Active, 
        OrderStatus.Processed,
        OrderStatus.Completed));

Understand that not all Linq providers like custom extension methods in their lambdas. NHibernate, for instance, won't correctly translate the In() function without additional coding to extend the expression parser, but Contains() works just fine. For Linq 2 Objects, no problems.

Solution 2

I have used this extension:

    public static bool IsIn<T>(this T value, params T[] list)
    {

                     return list.Contains(value);           
    }

You may use this as the condition:

   Where(x => x.IsIn(OrderStatus.Valid, ... )

Solution 3

If that set of statuses has some meaning, for example those are statuses for accepted orders, you can define an extension method on your enum and use that in your linq query.

public static class OrderStatusExtensions
{
    public static bool IsAccepted(this OrderStatuses status)
    {
        return status == OrderStatuses.Valid
            || status == OrderStatuses.Active
            || status == OrderStatuses.Processed
            || status == OrderStatuses.Completed;
    }
}

var acceptedOrders = from o in orders
                     where o.Status.IsAccepted()
                     select o;

Even if you could not give the method a simple name, you could still use something like IsValidThroughCompleted. In either case, it seems to convey a little more meaning this way.

Share:
16,052
Todd Davis
Author by

Todd Davis

Experienced, front-end focused web developer with 19+ years of experience designing responsive, clean and intuitive websites using modern JavaScript frameworks such as Angular. An expert at creating user interfaces that solve problems and streamline online process workflow, I follow a mantra of “Simplify and Empathize” in order to understand the challenges our customers face and how best to address them while opening new avenues of revenue and service for the company.

Updated on July 17, 2022

Comments

  • Todd Davis
    Todd Davis almost 2 years

    I have an enum called OrderStatus, and it contains various statuses that an Order can be in:

    • Created
    • Pending
    • Waiting
    • Valid
    • Active
    • Processed
    • Completed

    What I want to do is create a LINQ statement that will tell me if the OrderStaus is Valid, Active, Processed or Completed.

    Right now I have something like:

    var status in Order.Status.WHERE(status => 
          status.OrderStatus == OrderStatus.Valid || 
          status.OrderStatus == OrderStatus.Active|| 
          status.OrderStatus == OrderStatus.Processed|| 
          status.OrderStatus == OrderStatus.Completed)
    

    That works, but it's very "wordy". Is there a way to convert this to a Contains() statement and shorten it up a bit?

  • KeithS
    KeithS over 13 years
    Good answer for now, but it may become more brittle later. Understanding this code would require checking the enum to know ALL the codes that fall in the defined range, and the dev must always be able to define a continuous range containing exactly the codes desired (if a constant Shipped was placed between Processed and Completed that the user didn't want in their results, this code breaks).
  • p.campbell
    p.campbell over 13 years
    +1 for the extension method! I'd hope to refactor it to use LINQ within as well.
  • Aliostad
    Aliostad over 13 years
    Yes, I always wanted to do that but forgot :)
  • JaredPar
    JaredPar over 13 years
    @KeithS, yes, I covered that in the last paragraph of my answer.
  • Todd Davis
    Todd Davis over 13 years
    Yes, great idea. I might use something like this as well, pretty cool
  • Todd Davis
    Todd Davis over 13 years
    Yup, agreed, a little too flaky for my application, however I really like how you did this, as it makes the code a little more flexible. We could easily add a new Status without breaking the code for example.