arame3333
arame3333

Reputation: 10193

filtering a collection using Linq - how to reuse the same filter

I am filtering a collection and I perform 2 filters that are the same but for different fields. There must be a way I can reduce the duplication of code here? The checks are whether a date has been entered, and whether it is before a cut off date entered by the user.

    public override IList<Company> Search()
    {
        var list = CacheObjects.Subcontractors;

        this.ApplicationFormReturnedCheck(ref list);

        this.ApplicationFormSentCheck(ref list);
    }

    private void ApplicationFormReturnedCheck(ref IList<Subcontractor> list)
    {
        if (this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null)
        {
            list =
                list.Where(x => x.ApplicationFormReturned == null || x.ApplicationFormReturned < this.ApplicationFormReturned).ToList();
        }
        else if (this.ApplicationFormNotReturnedFlag == true)
        {
            list = list.Where(x => x.ApplicationFormReturned == null).ToList();
        }
        else if (this.ApplicationFormReturned != null)
        {
            list = list.Where(x => x.ApplicationFormReturned < this.ApplicationFormReturned).ToList();
        }
    }

    private void ApplicationFormSentCheck(ref IList<Subcontractor> list)
    {
        if (this.ApplicationFormNotSentFlag == true && this.ApplicationFormSent != null)
        {
            list =
                list.Where(x => x.ApplicationFormSent == null || x.ApplicationFormSent < this.ApplicationFormSent).ToList();
        }
        else if (this.ApplicationFormNotSentFlag == true)
        {
            list = list.Where(x => x.ApplicationFormSent == null).ToList();
        }
        else if (this.ApplicationFormSent != null)
        {
            list = list.Where(x => x.ApplicationFormSent < this.ApplicationFormSent).ToList();
        }
    }

Upvotes: 1

Views: 271

Answers (2)

Jamiec
Jamiec

Reputation: 136134

I would suggest you can do something as simple as have some instances of Func<Subcontractor,bool> which cover your various scenarios. This is the type of Func that the Where method expects

To demonstrate let me take one of your methods and show you how:

private void ApplicationFormReturnedCheck(ref IList<Subcontractor> list)
{
    var isFormReturned = new Func<Subcontractor,bool>(
          x => x.ApplicationFormReturned != null);
    var isBeforeDate = new Func<Subcontractor,bool>(
          x => x.ApplicationFormReturned < this.ApplicationFormReturned);
    var isFormReturnedOrBeforeDate= new Func<Subcontractor,bool>(
          x => isFormReturned(x) || isFormReturnedBeforeDate(x));

    if (this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null)
    {
        list = list.Where(isFormReturnedOrBeforeDate).ToList();
    }
    else if (this.ApplicationFormNotReturnedFlag == true)
    {
        list = list.Where(isFormReturned).ToList();
    }
    else if (this.ApplicationFormReturned != null)
    {
        list = list.Where(isBeforeDate).ToList();
    }
}

The other method you've shown, although having similar logic, uses a different set of variables. (ApplicationFormSent in place of ApplicationFormReturned). The way I see it you have two options

  1. Duplicate the above within the other method, using the differing variable names
  2. Use a more complex method whereby you have these 3 Func's outside of the scope of each method, and able to distinguish which variables (*Sent or *Returned) to use.

The problem with 2. above is that as your perceved "reuse" goes up, the readability of your code goes down.

In all honesty, I see no major problem with your original code! Its clear, its pretty concise and its easy to see what it's doing. By wrapping all this logic up with predicates (which could conceivably be elsewhere in the class), you're making it harder to read and harder to maintain.

Upvotes: 1

Metro Smurf
Metro Smurf

Reputation: 38375

A Func(T, TResult) can be used to encapsulate common predicate methods. In your case, the Func would need to be initilialized in the constructor since you are using instance members in the filter. Ex:

private readonly Func<Subcontractor, bool> _pred;

public Subcontractor()
{
    _pred = x => x.ApplicationFormReturned == null || x.ApplicationFormReturned < this.ApplicationFormReturned;
}

private void ApplicationFormReturnedCheck( ref IList<Subcontractor> list )
{
    if( this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null )
    {
        list = list.Where( _pred ).ToList();
    }
    else if( this.ApplicationFormNotReturnedFlag == true )
    {
        list = list.Where( x => x.ApplicationFormReturned == null ).ToList();
    }
    else if( this.ApplicationFormReturned != null )
    {
        list = list.Where( x => x.ApplicationFormReturned < this.ApplicationFormReturned ).ToList();
    }
}

Upvotes: 0

Related Questions