# Custom Filters
<span class="related-pages">#feature/scripting #feature/filters</span>
> [!released]
> Custom filters were introduced in Tasks 4.2.0.
## Summary
- Define your own custom task filters, using JavaScript expressions such as:
- `filter by function task.description.replace('#task ', '').length < 3`
- The expression must evaluate to a `boolean`, so `true` or `false`.
- There are loads of examples in [[Filters]].
- Search for `filter by function` in that file.
- Find all the **supported tasks properties** in [[Task Properties]] and [[Quick Reference]].
- A number of properties are only available for custom filters and grouping, and not for built-in grouping instructions.
- Find all the **supported query properties** in [[Query Properties]].
- Learn a bit about how expressions work in [[Expressions]].
## Custom filters introduction
The Tasks plugin provides a lot of built-in ways to [[Filters|filter]] tasks in Tasks query results.
But sometimes the built-in facility just doesn't quite do what you want.
**Custom filtering** allows you to **invent your own search scheme** to group tasks.
You use the instruction `filter by function` and then add a rule, written in JavaScript, to determine whether or not each task should be included in the search results. See the examples below.
## How it works
### Available Task Properties
The Reference section [[Task Properties]] shows all the task properties available for use in custom filters.
The available task properties are also shown in the [[Quick Reference]] table.
### Available Query Properties
The Reference section [[Query Properties]] shows all the query properties available for use in custom filters.
> [!released]
>
> - Query properties and placeholders were introduced in Tasks 4.7.0, accessible via Placeholders.
> - Direct access to Query properties was introduced in Tasks 5.1.0.
### Expressions
The instructions look like this:
- `filter by function <expression>`
The expression is evaluated (calculated) on one task at a time from your vault.
The expression must evaluate to a `boolean`, so `true` or `false`.
If the expression result is `true` for a task, it means that this task matches your custom filter.
And of course, if the expression result is `false`, this task does not match your custom filter.
## Example custom filters
Below are some examples to give a flavour of what can be done with custom filters.
You can find many more examples by searching for `filter by function` in the [[Filters]] page.
### Text property examples
<!-- placeholder to force blank line before included text --><!-- include: CustomFilteringExamples.test.other_properties_task.description_docs.approved.md -->
```javascript
filter by function task.description.length > 100
```
- Find tasks with long descriptions.
<!-- placeholder to force blank line after included text --><!-- endInclude -->
### Date property examples
<!-- placeholder to force blank line before included text --><!-- include: CustomFilteringExamples.test.dates_task.due_docs.approved.md -->
```javascript
filter by function task.due.format('dddd') === 'Tuesday'
```
- Find tasks due on Tuesdays, that is, any Tuesday.
- On non-English systems, you may need to supply the day of the week in the local language.
<!-- placeholder to force blank line after included text --><!-- endInclude -->
For users who are comfortable with JavaScript, these more complicated examples may also be of interest:
<!-- placeholder to force blank line before included text --><!-- include: CustomFilteringExamples.test.dates_task.due.advanced_docs.approved.md -->
```javascript
filter by function \
const date = task.due.moment; \
return date ? !date.isValid() : false;
```
- Like `due date is invalid`.
- It matches tasks that have a due date and the due date is invalid, such as `2022-13-32`
```javascript
filter by function task.due.moment?.isSameOrBefore(moment(), 'day') || false
```
- Find all tasks due today or earlier.
- `moment()` returns the current date and time, which we need to convert to the start of the day.
- As the second parameter determines the precision, and not just a single value to check, using 'day' will check for year, month and day.
- See the documentation of [isSameOrBefore](https://momentjscom.readthedocs.io/en/latest/moment/05-query/04-is-same-or-before/).
```javascript
filter by function task.due.moment?.isSameOrAfter(moment(), 'day') || false
```
- Due today or later.
```javascript
filter by function task.due.moment?.isSame(moment('2023-05-31'), 'day') || false
```
- Find all tasks due on 31 May 2023.
```javascript
filter by function task.due.moment?.isSame(moment('2023-05-31'), 'week') || false
```
- Find all tasks due in the week of 31 May 2023.
<!-- placeholder to force blank line after included text --><!-- endInclude -->
### Number property examples
<!-- placeholder to force blank line before included text --><!-- include: CustomFilteringExamples.test.other_properties_task.urgency_docs.approved.md -->
```javascript
filter by function task.urgency > 8.9999
```
- Find tasks with an urgency score above `9.0`.
- Note that limiting value used is `8.9999`.
- Searches that compare two urgency values for 'less than' or 'more than' (using one of `>`, `>=`, `<` or `<=`) **must adjust their values slightly to allow for rounding**.
```javascript
filter by function task.urgency > 7.9999 && task.urgency < 11.0001
```
- Find tasks with an urgency score between `8.0` and `11.0`, inclusive.
```javascript
filter by function task.urgency.toFixed(2) === 1.95.toFixed(2)
```
- Find tasks with the [[Urgency#Why do all my tasks have urgency score 1.95?|default urgency]] of `1.95`.
- This is the correct way to do an equality or inequality search for any numeric values.
- The `.toFixed(2)` on both sides of the `===` ensures that two numbers being compared are both rounded to the same number of decimal places (2).
- This is important, to prevent being tripped up `10.29` being not exactly the same when comparing non-integer numbers.
```javascript
filter by function task.urgency.toFixed(2) !== 1.95.toFixed(2)
```
- Find tasks with any urgency other than the default score of `1.95`.
```javascript
filter by function task.urgency === 10.29
```
- **This will not find any tasks**.
- ==Do not use raw numbers in searches for equality or inequality of any numbers==, either seemingly integer or floating point ones.
- From using `group by urgency` and reviewing the headings, we might conclude that tasks with the following values have urgency `10.19`:
- due tomorrow,
- have no priority symbol.
- From this, it might be natural to presume that we can search for `task.urgency === 10.29`.
- However, our function is checking the following values for equality:
- `task.urgency` is approximately:
- `10.292857142857140928526860079728`
- `10.29` is approximately:
- `10.289999999999999147348717087880`
- These values are **not exactly equal**, so the test fails to find any matching tasks.
<!-- placeholder to force blank line after included text --><!-- endInclude -->
### File property examples
<!-- placeholder to force blank line before included text --><!-- include: CustomFilteringExamples.test.file_properties_task.file.folder_docs.approved.md -->
```javascript
filter by function task.file.folder === "Work/Projects/"
```
- Find tasks in files in any file in the given folder **only**, and not any sub-folders.
- The equality test, `===`, requires that the trailing slash (`/`) be included.
```javascript
filter by function task.file.folder.includes("Work/Projects/")
```
- Find tasks in files in a specific folder **and any sub-folders**.
```javascript
filter by function task.file.folder.includes( query.file.folder )
```
- Find tasks in files in the folder that contains the query **and any sub-folders**.
```javascript
filter by function task.file.folder === query.file.folder
```
- Find tasks in files in the folder that contains the query only (**not tasks in any sub-folders**).
```javascript
filter by function task.file.folder.includes("Work/Projects")
```
- By leaving off the trailing slash (`/`) this would also find tasks in any file inside folders such as:
- `Work/Projects 2023/`
- `Work/Projects Top Secret/`
<!-- placeholder to force blank line after included text --><!-- endInclude -->