Mr Jones
Mr Jones

Reputation: 1198

LINQ where clause- compare collection of strings

I have a variable number of "filters" to apply to an entity collection, and these filters are stored in a List. Right now, I'm doing the following:

    IQueryable<Items> items = SharedContext.Context.Items.GetAll();
    //This list is dynamic, but shown static here for simplicity
    IEnumerable<Items> filterList = new List<string>(){"new", "old", "current"};
    IEnumerable<Item> items_temp = new List<Item>();
    foreach (string type in filterList)
    {
        var temp = items.Where(i => i.Type.ToLower().Trim() == type.ToLower().Trim());
        items_temp = items_temp.Union(temp);
    }
    items = items_temp.AsQueryable();

Unfortunately, this causes a huge performance issue. I know somebody out there has a better solution... What do you guys think?

EDIT
Running my application with the code above takes around 30 seconds to execute, but if do the following:

    items.Where(item => item.Type.ToLower().Trim() == "new" ||
                        item.Type.ToLower().Trim() == "old" ||
                        item.Type.ToLower().Trim() == "current");

my application executes in about 4 seconds. Can anyone think of a solution that can match this performance or at least fill me in on why the results are so drastically different? FYI, I'm binding my data to a grid with multiple grids nested inside... a small improvement can go a long way.

Upvotes: 2

Views: 3410

Answers (2)

p.s.w.g
p.s.w.g

Reputation: 149010

Sounds like what you want is this:

var items = SharedContext.Context.Items.GetAll();
IEnumerable<string> filterList = new List<string>(){"new", "old", "current"};
var filteredItems = items.Where(i => filterList.Contains(i.Type.ToLower()));

If you're having issues with this, you might want to try using an array instead:

string[] filterList = new string[] {"new", "old", "current"};
var filteredItems = items.Where(i => filterList.Contains(i.Type.ToLower()));

Update: Here's another strategy which generates a filter expression dynamically:

var filterList = new[] { "new", "old", "current" };
var param = Expression.Parameter(typeof(Item));
var left = 
    Expression.Call(
        Expression.Call(
            Expression.PropertyOrField(param, "Type"),
            typeof(string).GetMethod("ToLower", Type.EmptyTypes)),
        typeof(string).GetMethod("Trim", Type.EmptyTypes));
var filterExpr = (Expression<Func<Item, bool>>)Expression.Lambda(
    filterList
        .Select(f => Expression.Equal(left, Expression.Constant(f)))
        .Aggregate((l, r) => Expression.OrElse(l, r)), 
    param);
var filteredItems = items.Where(filterExpr);

Upvotes: 4

D Stanley
D Stanley

Reputation: 152521

You could do a join to filter the items:

IEnumerable<Items> filterList = new List<string>(){"new", "old", "current"};          
IQueryable<Items> items = SharedContext.Context.Items.GetAll();

var filteredItems = from i items
                    join f in filterList 
                      on i.Type.ToLower().Trim() equals t.ToLower().Trim()
                    select i;

Upvotes: 2

Related Questions