I have a class
public class TestClass
{
public int? a { get; set; }
public int? b { get; set; }
public TestClass(int _a, int _b)
{
a = _a;
b = _b;
}
}
And for this class, I generate a list with elements of it.
List<TestClass> lista = new List<TestClass>
{
new TestClass(1,2),
new TestClass(2,3),
new TestClass(1,3)
};
Now, I would like to generate a method that, depending on the options that the user selects, allows me to return either all the elements of the list (if I do not select anything), or those that comply with what the user selected.
If I have that class with only those two properties, I could do something like this:
List<TestClass> resul;
if (seleccionA == null && seleccionB == null)
{
resul = lista;
}
if (seleccionA == null && seleccionB != null)
{
resul = lista.Where(v => seleccionB == v.b).ToList();
}
if (seleccionA != null && seleccionB == null)
{
resul = lista.Where(v => seleccionA == v.a).ToList();
}
if (seleccionA != null && seleccionB != null)
{
resul = lista.Where(v => seleccionA == v.a && seleccionB == v.b).ToList();
}
But if there are more than two properties, the if is getting worse. How can I solve this in a more efficient way of writing in the code?
For these cases we can make use of the anonymous functions and build a predicate that changes depending on the selected options.
To do this, we will define the following:
And now, for each value that we want to be added (or not) to the predicate, we are going to do the following:
And let's write a function to parse the predicate and return the necessary list:
This way, if we need to add more conditions, we just have to add it to the predicate we're building.
ArmarPredicado is an anonymous function that returns exactly what the where needs, which is a condition and the value that it will return.
When we pass it
v => (valora == v.a)
, it transforms that to another anonymous function, and it is concatenating those functions.As the definition of where says
it receives a Func<,>, which is exactly what we declare as a predicate. and the other thing we did was to concatenate them according to need.
here is my answer using expressions:
First we import the following namespaces:
Then I recommend also creating an alias for this type:
And the ArmPredicate method would look like this:
And you use it like this:
If value and valueb are null, it returns the entire source list, and if not, it creates the filter with those that are not null.
In this way you do not have to create the predicate with if's, a single line creates the predicate for you, you only pass the pair of objects that is, the one that contains the value that you are going to filter, and the expression that indicates with which property it is going to be filtered. compare the object.
And since you're passing an array in params format, you can add as many "filter -> property" pairs as you need.
With C# 7 and the ValueTuple type it looks even better.
EDIT: The only bad thing is that it is only for equality, that is, it only checks if they are equal, a more structured filter is not valid, although that can be solved.
EDIT: An idea so that it can be created with any expression, not just equality:
If you are interested I can create the class and edit and put it.