c#
Linq: Filter list of type A based on an object of type A?
I have a List of objects of type Foo, and another instance of an object of type Foo. I would like to use linq to filter the list based on the non-null properties of the instance. class Foo { public int ID; public string Description; public long Location; } Foo fooFilter = new Foo() { ID = null, Description = null, Location = 1 } List<Foo> fooList = new List<Foo>(); fooList.Add(new Foo(){ID = 1, Description = "one", Location = 1}); fooList.Add(new Foo(){ID = 2, Description = "two", Location = 0}); fooList.Add(new Foo(){ID = 3, Description = "three", Location = 1}); List<Foo> filteredFooList = fooList.Where(???); I want to somehow use fooFilter to query fooList and fill filteredFooList with: [ {ID = 1, Description = "one", Location = 1}, {ID = 3, Description = "three", Location = 1} ] EDIT: I was trying to be brief to make the question more clear, but I might have left of important information. In my actual program, List<Foo> is a large result from a database (over 40k entries). I'm trying to make a controller method (MVC) which can take any combination of parameters that would match the field names of an entity framework object. So <Foo> is a EF record type. So I'm trying to avoid having to explicitly list all the (15 or so) fields that can be filtered on in the controller: public class Home : Controller { public ActionResult FilteredFooList(int ID, string Description, long Location, etc, etc, etc) { } } And do something more like: public class Home : Controller { public ActionResult FilteredFooList(Foo filterObj) { } } Maybe this isn't possible or a good idea?
If you only want to filter out non instanciated properties, you don't need a filter. class Foo { public int ID; public string Description; public long Location; public bool IsInstanciated() { return this.ID != default(int) && this.Description != default(string) && this.Location != default(long); } } List<Foo> filteredFooList = fooList.Where(f => f.IsInstanciated()); If you really need to use that instanciated class as a filter, I'd suggest you to use IEquatable<T> class Foo : IEquatable<Foo> { public int ID; public string Description; public long Location; public bool Equals(Foo other) { // Whatever your logic is return this.Description == other.Description && this.ID == other.ID && this.Location == other.Location; } } List<Foo> filteredFooList = fooList.Where(f => !f.Equals(fooFilter));
Assuming you can change your value types to be nullable: class Foo { public int? ID; public string Description; public long? Location; } Then you can use some extensions: public static class Ext { public static bool EqualOrNull<T>(this T? value, T? filter) where T : struct, IComparable { return (filter == null) || (value.Value.CompareTo(filter.Value) == 0); } public static bool EqualOrNull<T>(this T value, T filter) where T : class, IComparable { return (filter == null) || (value.CompareTo(filter) == 0); } } To do this: var filteredFooList = fooList.Where(f => f.ID.EqualOrNull(fooFilter.ID) && f.Description.EqualOrNull(fooFilter.Description) && f.Location.EqualOrNull(fooFilter.Location)); If you wanted something truly generic (e.g. not dependent on knowing the field names) you would need to step into the world of Reflection.
By looking at your expected output, it appears that you want to filter fooList so that you get all items that have the same Location as your fooFilter object. If that's what you're asking, you can do this: List<Foo> filteredFooList = fooList.Where(item => item.Location == fooFilter.Location);
Don't paint yourself into a corner by adding unnecessary properties to your data objects, they should stay data objects. You're effectively trying to build a dynamic query where you want to conditionally filter by a list of conditions. There are patterns to do this. Start with a base query, then determine if you want to filter by one of the properties. Do the same with the rest of the other properties. By the time you reach the end, you could then just gather the results. var filter = new Foo { ID = null, Description = null, Location = 1, }; var data = new List<Foo> { new Foo { ID = 1, Description = "one", Location = 1 }, new Foo { ID = 2, Description = "two", Location = 0 }, new Foo { ID = 3, Description = "three", Location = 1 }, }; var query = data.AsEnumerable(); if (filter.ID != null) query = query.Where(x => x.ID == filter.ID); if (filter.Description != null) query = query.Where(x => x.Description == filter.Description); if (filter.Location != null) query = query.Where(x => x.Location == filter.Location); var result = query.ToList(); This assumes that ID and Location are actually nullable just like your example implies. public class Foo { public int? ID { get; set; } public string Description { get; set; } public long? Location { get; set; } }
Related Links
Bot Framework Channel Emulator has invalid channel ID?
Memory trouble with recursive functions C#
cannot evaluate the item metadata
What Dependency Injection Framework Does ASP.Net Core Use?
Retrieve Type Foo<Bar> from name when Bar is created from TypeBuilder
Is it possible to create a “perfect forwarding” generic collection in C#?
Forcing the UI Rendering after I performed an action in UWP
Coding: changing a and b values [closed]
Incorrect syntax near 'ENTUserAccount'
Change the color of the Grid Cell in C#
DynamicResource doesn't show the correct value
how to pass unicode in asp.net web api to sql server database [duplicate]
C# - Access property in inheriting class
Sending bulk push notifications using FCM in C#?
Retrieve data from jQuery POST in aspx
strange behaviour of MVC routing