Beyond Basic Filters: Date Multi-Select Filters
This is the second article in the series "Beyond Basic Filters," where we explore different aspects of filters that are integral parts of applications we develop. This article focusses on dates filters and builds on ideas from previous posts in the series, so be sure to check those out!
Why a Multi-Select Filter for Dates?
Let's explore why we would want to use a Multi-Select filter for dates before diving into the how. The most straightforward solution for a date filter is a range filter—letting users select a start and/or end date to show entries within that range. If showing specific date ranges meets all user needs, then a range filter is clearly the best choice.
For inspiration, let's look at Microsoft Excel, which, despite its quirks, is well-established and designed for diverse needs. While Excel offers complex column filters with "before," "after," and "between" constraints, it also displays a list of available values in each column. Interestingly, dates (even in mixed-type columns) are automatically grouped by year and month. This approach offers two key benefits:
- Users can easily see the range of available data—specifically, which years contain data in the dataset.
- Users can quickly select or deselect specific entries to focus on or exclude particular years, months, or days without crafting complex filter rules.
These benefits extend beyond dates to all data types, making Multi-Select filters for dates worth exploring in detail.
Grouping Resolution
When implementing a Multi-Select filter for dates, grouping resolution is a crucial consideration. Similar to Excel's year-and-month grouping approach, we must determine the appropriate level of detail for presenting date options to users.
This resolution choice significantly affects usability of our filters. Grouping dates too finely can overwhelm users with too many choices, while overly broad grouping may limit the filter's usefulness. Based on the use case and how the data is spread out, you might group dates by year, year-month, or year-quarter.
Filling Gaps when generating Filter Values
Users of our application encountered an issue with date filters. When configuring column filters in data grids, users wanted to view data from specific years. Our date Multi-Select filters displayed month-based values grouped by year, allowing users to unselect all data and select their year of interest. Since users can save and reload these configured views, we needed to consider how to persist the filter values. As discussed in the previous article, Multi-Select filters store selected items as a list or set data structure. However, when a year group is selected with only certain months present, only those existing month entries are saved in the filter state. This means that if new data is added for that year, the new months won't appear because they weren't explicitly selected.
A rule-based filter could solve this by directly storing the user's intent (e.g., "only show data for year 2025"). We explored ways to extend our multi-select filter to enable more reliable year-based filtering. Our solution was to generate filter values that show and store all 12 months for a year whenever any data exists for that year.
This compromise means users can no longer see which specific months contain data by looking at the multi-select filter values. However, they can still use the shown years to gauge the data range, sacrificing only monthly resolution. Most importantly, users can now reliably include data for an entire year without worrying that an expanded dataset might break their persisted filter.
Interaction with Exclusion Filters
The concept of exclusion filters, discussed in our previous article, applies to all Multi-Select filters regardless of data type or grouping levels. Initially, we worried that exclusion filters might conflict with our custom data extension (where we add months to the filter even when no actual data exists for those months). However, after analysis, we found that these two concepts operate entirely independently.
Let's explore this with an example: When using an exclusion filter to show all data except for a specific year, the filter will correctly exclude all months of that year, including those we added through our data extension. Similarly, when excluding specific months, the filter operates on the actual data, unaffected by our year-based grouping approach.
Why not store group selection in a multi-select filter?
A natural question arises: Why not simply store selected groups (like years) in the filter state, rather than individual month entries? While this approach seems more intuitive, it would significantly complicate our filter implementation. The filter would need to maintain two separate selection mechanisms—one for groups and another for individual entries—and handle complex interactions between them. While we could implement this more complex filter state, our current approach—extending the data to include all months within a year—achieves the same result with a simpler, more consistent implementation. Grouping serves primarily as a visual feature in the filter's UI, acting as a shortcut for selecting or deselecting all items within a group.
Concluding Thoughts
Multi-Select filters for dates offer a powerful and intuitive way for users to interact with date-based data. While they present unique challenges—particularly around grouping resolution and data persistence—these can be effectively addressed through careful design decisions.
Our solution of extending date entries to include all months within years containing data strikes a balance between usability and implementation complexity. This approach maintains the simplicity of Multi-Select filters while providing reliable year-based filtering capabilities that users can trust to work consistently as their datasets grow.
When implementing date filters in your applications, consider whether your users need precise date range selection or would benefit from the flexibility and discoverability that Multi-Select filters provide. The choice ultimately depends on your specific use case, but understanding these tradeoffs will help you make more informed decisions in your filter implementations.