silhouette hustler
silhouette hustler

Reputation: 1763

The best way to filter a list based on multiple check-boxes selected

At the moment I get the number of possible combinations (Factorial of the number of check-boxes) and write that many if statements, something like:

Assuming I have 3 check-boxes:

if (IncludeIncomingCalls && !IncludeOutgoingCalls && !IncludeExternalCalls)
            {
                return _callsData.Where(x => x.IncomingCall && !x.OutgoingCall && !x.ExternalCall);
            }
            if (!IncludeIncomingCalls && IncludeOutgoingCalls && !IncludeExternalCalls)
            {
                return _callsData.Where(x => !x.IncomingCall && x.OutgoingCall && !x.ExternalCall);
            }
            if (!IncludeIncomingCalls && !IncludeOutgoingCalls && IncludeExternalCalls)
            {
                return _callsData.Where(x => !x.IncomingCall && !x.OutgoingCall && x.ExternalCall);
            }
            if (IncludeIncomingCalls && IncludeOutgoingCalls && !IncludeExternalCalls)
            {
                return _callsData.Where(x => x.IncomingCall && x.OutgoingCall && !x.ExternalCall);
            }
            if (IncludeIncomingCalls && !IncludeOutgoingCalls && IncludeExternalCalls)
            {
                return _callsData.Where(x => x.IncomingCall && !x.OutgoingCall && x.ExternalCall);
            }
            if (!IncludeIncomingCalls && IncludeOutgoingCalls && IncludeExternalCalls)
            {
                return _callsData.Where(x => !x.IncomingCall && x.OutgoingCall && x.ExternalCall);
            }

Even though this will meet the requirement I don't see it as an optimal solution considering that the number of check-boxes might increase in the future and the number of the combinations could get massive.

I was wandering if there is a known pattern when it comes to filtering lists based on selected check-boxes?

Upvotes: 0

Views: 74

Answers (4)

InBetween
InBetween

Reputation: 32740

You could use the following pattern:

//some checkboxes
CheckBox chkA = ...
CheckBox chkB = ...
CheckBox chkC = ...

//build up your filters
var filters = new List<Predicate<SomeEntity>>();
filters.Add(e => chkA.Checked && e.IsA);
filters.Add(e => chkB.Checked && e.IsB);
filters.Add(e => chkC.Checked && e.IsC);

//And now simply apply the filters
var entities = ... //some enumerable of SomeEntity
var filteredEntities = entities.Where(e => filters.All(filter => filter(e)));

Note that this will only work correctly if IsA, IsB and IsC are excluding conditions, but this seems to be the set up you currently have.

Upvotes: 0

Georg Patscheider
Georg Patscheider

Reputation: 9463

Note that you can compose the IQueryable. You can add additional Where clauses as needed.

var result = callsData.Select(x => x);

if (IncludeIncomingCalls) {
    result = result.Where(x => x.IncomingCall);
}
else {
    result = result.Where(x => !x.IncomingCall);
}

if (IncludeOutgoingCalls) {
    result = result.Where(x => x.OutgoingCall);
}
else {
    result = result.Where(x => !x.OutgoingCall);
}

if (IncludeExternalCalls) {
    result = result.Where(x => x.ExternalCall);
}
else {
    result = result.Where(x => !x.ExternalCall);
}

return result;

I just show this as a general pattern. For your usecase, the solution of Ubiquitous Developers is easier to read and understand.

But if the condition is more complex than just a bit flag, this pattern might come in handy. Just as an example:

if (ShowOnlyActive) {
    result = result.Where(x => x.State == CallState.Active);
}
else {
    result = result.Where(x => x.State == CallState.Deleted || x.State == CallState.Inactive);
}

Off topic, just to further illustrate this general concept: adding additional clauses to an IQueryable can be used to refactor parts of a query to helper or extension methods, for example to implement paging.

public interface IPageableQuery {
    // The page size (i.e. the number of elements to be displayed).
    // The method processing the Query will Take() this number of elements.
    int DisplayLength { get; set; }

    // The number of elements that have already been displayed.
    // The method processing the Query will Skip() over these elements.
    int DisplayStart { get; set; }
}

public static IQueryable<T> ApplyPaging<T>(this IQueryable<T> entries, IPageableQuery query)
    where T : class {
    if (query.DisplayStart >= 0 && query.DisplayLength > 0) {
        return entries.Skip(query.DisplayStart).Take(query.DisplayLength);
    }
    return entries;
}

Upvotes: 0

Kadi Okba abdelmoumen
Kadi Okba abdelmoumen

Reputation: 59

Try this :

return _callsData.Where(x => x.IncomingCall==IncludeIncomingCalls  && x.OutgoingCall==IncludeOutgoingCalls  && x.ExternalCall==IncludeExternalCalls);

Upvotes: 0

Hardik Gondalia
Hardik Gondalia

Reputation: 3717

Compare the Boolean value with each field.
Try this:

return _callsData.Where(x => x.IncomingCall == IncludeIncomingCalls  && x.OutgoingCall == IncludeOutgoingCalls && x.ExternalCall== IncludeExternalCalls);

Upvotes: 1

Related Questions