Home > Software engineering >  How to apply OR condition in LINQ for generic types
How to apply OR condition in LINQ for generic types

Time:03-19

I need to filter a list of generic objects (TItem) which may contain different string properties, like "Name", "Description", "Email", "Street" etc. Depending on the object type I can pass a list of search keys (strings) for a filtering purpose. In the below example for the current object, I pass two search keys, which later I use to get property values by names. But for another object, it can be any other number of search keys with different names. When I know the number of search keys in advance I can hardcode it as below:

var searchString = "john";    
var propertyNames = new List<string>() { "Name", "Email" };
var originalList = new List<TItem>();
var filteredList = new List<TItem>();
    
// don't know how to apply OR (||) condition in below Where() method depending on how many search keys I might have
// I need to dynamically loop through searchKeys inside of Where()
    
filteredList = originalList.Where(e =>
        e.GetType().GetProperty(propertyNames[0]).GetValue(e).ToString().Contains(searchString) ||
        e.GetType().GetProperty(propertyNames[1]).GetValue(e).ToString().Contains(searchString)
    ).ToList();

But I cannot figure out how to loop through search keys inside of LINQ dynamically. Is it possible to add "OR" conditions dynamically using any loop technique? Other approaches also can be offered. The interface approach will not work here, as property names are totally different.

Update: For now, I resolved it using nested foreach loops. But for me, it looks very dirty. And I suppose the current approach has a big performance hit, as the same list will be iterated several times - for every property name

filteredList = new List<TItem>();
foreach (var property in propertyNames)
{
    foreach (var item in originalList)
    {
        var match = item.GetType().GetProperty(property).GetValue(item).ToString().ToLower().Contains(searchString.ToLower());
        if (match && !filteredList.Contains(item))
        {
            filteredList.Add(item);
        }
    }
}

Still looking for a cleaner and more concise solution. Preferably with LINQ

CodePudding user response:

I think linq Any extension fits this requirement.

filteredList = originalList
                .Where(e => propertyNames.Any(p =>
        e.GetType().GetProperty(p).GetValue(e).ToString().Contains(searchString) 
      )).ToList();
  • Related