--- id: id-generated-by-omnivore title: Grouping - Tasks User Guide - Obsidian Publish tags: - obsidian-tasks state: ARCHIVED date_saved: 2023-07-16 10:23:40 date_read: 2023-07-16 12:18:14 date_archived: 2023-07-16T10:05:16.592Z --- # Grouping - Tasks User Guide - Obsidian Publish #Omnivore [Read on Omnivore](https://omnivore.app/) [Read Original](https://publish.obsidian.md/tasks/Queries/Grouping) ## Webpage ## Grouping ## Contents This page is long. Here are some links to the main sections: * [Basics](https://publish.obsidian.md/tasks/Queries/Grouping#Basics) * [Custom Groups](https://publish.obsidian.md/tasks/Queries/Grouping#Custom%20Groups) * [Group by Task Statuses](https://publish.obsidian.md/tasks/Queries/Grouping#Group%20by%20Task%20Statuses) * [Group by Dates in Tasks](https://publish.obsidian.md/tasks/Queries/Grouping#Group%20by%20Dates%20in%20Tasks) * [Group by Other Task Properties](https://publish.obsidian.md/tasks/Queries/Grouping#Group%20by%20Other%20Task%20Properties) * [Group by File Properties](https://publish.obsidian.md/tasks/Queries/Grouping#Group%20by%20File%20Properties) * [Multiple groups](https://publish.obsidian.md/tasks/Queries/Grouping#Multiple%20groups) * [Refining groups](https://publish.obsidian.md/tasks/Queries/Grouping#Refining%20groups) * [Notes](https://publish.obsidian.md/tasks/Queries/Grouping#Notes) * [Screenshots](https://publish.obsidian.md/tasks/Queries/Grouping#Screenshots) * [Examples](https://publish.obsidian.md/tasks/Queries/Grouping#Examples) ## Basics Introduced in Tasks 1.6.0. By default, Tasks displays tasks in a single list. To divide the matching tasks up with headings, you can add `group by` lines to the query. ## Custom Groups `group by function` was introduced in Tasks 4.0.0. Tasks provides many built-in grouping options, but sometimes they don't quite do what is wanted by all users. Now Tasks has a powerful mechanism for you to create your own **custom groups**, offering incredible flexibility. There are many examples of the custom grouping instruction `group by function` in the documentation below, with explanations, for when the `group by` instructions built in to Tasks do not satisfy your preferences. You can find out more about this very powerful facility in [Custom Grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping). ## Group by Task Statuses For more information, including adding your own customised statuses, see [Statuses](https://publish.obsidian.md/tasks/Getting+Started/Statuses). ### Status * `group by status` (Done or Todo, which is capitalized for visibility in the headings) * Note that the Done group is displayed before the Todo group, which differs from the Sorting ordering of this property. * `Done` is used for tasks status types `DONE`, `CANCELLED` and `NON_TASK` * `Todo` is used for status types with type `TODO` and `IN_PROGRESS` Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by status** is now possible. * `group by function task.isDone ? "Action Required" : "Nothing To Do"` * Use JavaScript's ternary operator to choose what to do for true (after the ?) and false (after the :) values. ### Status Name * `group by status.name` * This groups by the names you give to your custom statuses, in alphabetical order. `group by status.name` was introduced in Tasks 1.23.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by status names** is now possible. * `group by function task.status.name` * Identical to "group by status.name". * `group by function task.status.name.toUpperCase()` * Convert the status names to capitals. ### Status Type * `group by status.type` * This groups by the types you have given to your custom statuses. * The groups will appear in this order, and with these group names: * `IN_PROGRESS` * `TODO` * `DONE` * `CANCELLED` * `NON_TASK` `group by status.type` was introduced in Tasks 1.23.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by status types** is now possible. * `group by function task.status.type` * Unlike "group by status.type", this sorts the status types in alphabetical order. ### Status Symbol There is no built-in instruction to group by status symbols. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by status symbol** is now possible. * `group by function "Status symbol: " + task.status.symbol.replace(" ", "space")` * Group by the status symbol, making space characters visible. ### Next Status Symbol There is no built-in instruction to group by next status symbols. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by next status symbol** is now possible. * `group by function "Next status symbol: " + task.status.nextSymbol.replace(" ", "space")` * Group by the next status symbol, making space characters visible. ## Group by Dates in Tasks ### Due Date * `group by due` * The due date of the task, including the week-day, or `No due date`. * `due` grouping option was introduced in Tasks 1.7.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by due date** is now possible. These examples all use `task.due` property, which is a `TasksDate` object. You can see the current [TasksDate source code](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/src/Scripting/TasksDate.ts), to explore its capabilities. * `group by function task.due.format("YYYY-MM-DD dddd")` * Like "group by due", except it uses no heading, instead of a heading "No due date", if there is no due date. * `group by function task.due.formatAsDate()` * Format date as YYYY-MM-DD or empty string (so no heading) if there is no due date. * `group by function task.due.formatAsDateAndTime()` * Format date as YYYY-MM-DD HH:mm or empty string if no due date. * Note: * This is shown for demonstration purposes. * Currently the Tasks plugin does not support storing of times. * Do not add times to your tasks, as it will break the reading of task data. * `group by function task.due.format("YYYY[%%]-MM[%%] MMM", "no due date")` * Group by month, for example `2023%%-05%% May` ... * ... which gets rendered by Obsidian as `2023 May`. * Or show a default heading "no due date" if no date. * The hidden month number is added, commented-out between two `%%` strings, to control the sort order of headings. * To escape characters in format strings, you can wrap the characters in square brackets (here, `[%%]`). * `group by function task.due.format("YYYY[%%]-MM[%%] MMM [- Week] WW")` * Group by month and week number, for example `2023%%-05%% May - Week 22` ... * ... which gets rendered by Obsidian as `2023 May - Week 22`. * If the month number is not embedded, in some years the first or last week of the year is displayed in a non-logical order. DON'T PANIC! For users who are comfortable with JavaScript, these more complicated examples may also be of interest: * `group by function task.due.moment?.fromNow() || ""` * Group by the time from now, for example "8 days ago". * Because Moment.fromNow() is not provided by TasksDate, we need special code for when there is no date value. * Whilst interesting, the alphabetical sort order makes the headings a little hard to read. * `group by function task.due.format("dddd")` * Group by day of the week (Monday, Tuesday, etc). * The day names are sorted alphabetically. * `group by function task.due.format("[%%]d[%%]dddd")` * Group by day of the week (Sunday, Monday, Tuesday, etc). * The day names are sorted in date order, starting with Sunday. * `group by function task.due.moment ? ( task.due.moment.day() === 0 ? task.due.format("[%%][8][%%]dddd") : task.due.format("[%%]d[%%]dddd") ) : "Undated"` * Group by day of the week (Monday, Tuesday, etc). * The day names are sorted in date order, starting with Monday. * Tasks without due dates are displayed at the end, under a heading "Undated". * This is best understood by pasting it in to a Tasks block in Obsidian and then deleting parts of the expression. * The key technique is to say that if the day is Sunday (`0`), then force it to be displayed as date number `8`, so it comes after the other days of the week. * `group by function (!task.due.moment) ? '%%4%% Undated' : result = task.due.moment.isBefore(moment(), 'day') ? '%%1%% Overdue' : result = task.due.moment.isSame(moment(), 'day') ? '%%2%% Today' : '%%3%% Future'` * Group task due dates in to 4 broad categories: `Overdue`, `Today`, `Future` and `Undated`, displayed in that order. * Try this on a line before `group by due` if there are a lot of due date headings, and you would like them to be broken down in to some kind of structure. * A limitation of Tasks expressions is that they each need to fit on a single line, so this uses nested ternary operators, making it powerful but very hard to read. * In fact, for ease of development and testing, it was written in a full-fledged development environment as a series of if/else blocks, and then automatically refactored in these nested ternary operators. * `group by function (!task.due.moment) ? '%%4%% ==Undated==' : result = task.due.moment.isBefore(moment(), 'day') ? '%%1%% ==Overdue==' : result = task.due.moment.isSame(moment(), 'day') ? '%%2%% ==Today==' : '%%3%% ==Future=='` * As above, but the headings `Overdue`, `Today`, `Future` and `Undated` are highlighted. * See the sample screenshot below. ![Tasks grouped by due date category, and then by due date](https://proxy-prod.omnivore-image-cache.app/600x0,siPKGht3mglkuseFvi45tMvJ_hZGufA4LlWk_Wo6L4M4/https://publish-01.obsidian.md/access/40e62a316a834ff6f495ebf1d122cae6/images/tasks_custom_groups_categorise_dates.png) Sample image showing tasks grouped first by highlighted words `Overdue`, `Today`, `Future` and `Undated`, and then by individual due date. ### Done Date * `group by done` * The done date of the task, including the week-day, or `No done date`. * `done` grouping option was introduced in Tasks 1.7.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by done date** is now possible. * `group by function task.done.format("YYYY-MM-DD dddd")` * Like "group by done", except it uses an empty string instead of "No done date" if there is no done date. ### Scheduled Date * `group by scheduled` * The scheduled date of the task, including the week-day, or `No scheduled date`. * `scheduled` grouping option was introduced in Tasks 1.7.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by scheduled date** is now possible. * `group by function task.scheduled.format("YYYY-MM-DD dddd")` * Like "group by scheduled", except it uses an empty string instead of "No scheduled date" if there is no scheduled date. ### Start Date * `group by start` * The start date of the task, including the week-day, or `No start date`. * `start` grouping option was introduced in Tasks 1.7.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by start date** is now possible. * `group by function task.start.format("YYYY-MM-DD dddd")` * Like "group by start", except it uses an empty string instead of "No start date" if there is no start date. * `group by created` * The created date of the task, including the week-day, or `No created date`. `created` grouping option was introduced in Tasks 2.0.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by created date** is now possible. * `group by function task.created.format("YYYY-MM-DD dddd")` * Like "group by created", except it uses an empty string instead of "No created date" if there is no created date. ### Happens * `group by happens` * The earliest of start date, scheduled date, and due date, including the week-day, or `No happens date` if none of those are set. `happens` grouping option was introduced in Tasks 1.11.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by happens date** is now possible. * `group by function task.happens.format("YYYY-MM-DD dddd")` * Like "group by happens", except it uses an empty string instead of "No happens date" if there is no happens date. ## Group by Other Task Properties As well as the date-related groups above, groups can be created from properties in individual tasks. ### Description There is no built-in instruction to group by description. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by description** is now possible. * `group by function task.description` * group by description. * This might be useful for finding completed recurrences of the same task. * `group by function task.description.toUpperCase()` * Convert the description to capitals. * `group by function task.description.slice(0, 25)` * Truncate descriptions to at most their first 25 characters, and group by that string. * `group by function task.description.replace('short', '==short==')` * Highlight the word "short" in any group descriptions. ### Description without tags Since Tasks 4.2.0, it is possible to remove tags from the descriptions in custom groups, for use in **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping)**. The value `task.descriptionWithoutTags` returns a copy of the description with all the tags removed, so that you can group together any tasks whose descriptions differ only by their tags. * `group by function task.descriptionWithoutTags` * Like `group by description`, but it removes any tags from the group headings. * This might be useful for finding completed recurrences of the same task, even if the tags differ in some recurrences. ### Priority * `group by priority` * The priority of the task, namely one of: * `Highest priority` * `High priority` * `Medium priority` * `Normal priority` * `Low priority` * `Lowest priority` * `priority` grouping option was introduced in Tasks 1.11.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by priority name and number** is now possible. Using the priority name: * `group by function task.priorityName` * Group by the task's priority name. * The priority names are displayed in alphabetical order. * Note that the default priority is called 'Normal', as opposed to with `group by priority` which calls the default 'None'. * `group by function '%%' + task.priorityNumber.toString() + '%%' + task.priorityName +' priority'` * Group by the task's priority name. * The hidden priority number ensures that the headings are written from highest to lowest priority. * Note that the default priority is called 'Normal', as opposed to with `group by priority` which calls the default 'None'. Using the priority number: * `group by function task.priorityNumber` * Group by the task's priority number, where Highest is 0 and Lowest is 5. ### Urgency * `group by urgency` ([urgency](https://publish.obsidian.md/tasks/Advanced/Urgency)) * The groups run from the highest urgency to the lowest. * You can reverse this with `group by urgency reverse`. * `urgency` grouping option was introduced in Tasks 3.6.0. * In Tasks 4.0.0 the order of `group by urgency` was reversed, to put most urgent tasks first. Add or remove the word `reverse` to get the original order. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by urgency** is now possible. * `group by function task.urgency.toFixed(3)` * Show the urgency to 3 decimal places, unlike the built-in "group by urgency" which uses 2. ### Recurrence * `group by recurring` * Whether the task is recurring: either `Recurring` or `Not Recurring`. * `group by recurrence` * The recurrence rule of the task, for example `every week on Sunday`, or `None` for non-recurring tasks. * Note that the text displayed is generated programmatically and standardised, and so may not exactly match the text in any manually typed tasks. For example, a task with `🔁 every Sunday` is grouped in `every week on Sunday`. * `recurring` and `recurrence` grouping options were introduced in Tasks 1.11.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by recurrence** is now possible. * `group by function task.isRecurring ? "Recurring" : "Non-Recurring"` * Use JavaScript's ternary operator to choose what to do for true (after the ?) and false (after the :) values. * `group by function task.recurrenceRule.replace('when done', '==when done==')` * Group by recurrence rule, highlighting any occurrences of the words "when done". ### Tags * `group by tags` * The tags of the tasks or `(No tags)`. If the task has multiple tags, it will show up under every tag. * `tags` grouping option was introduced in Tasks 1.10.0. * `group by function task.tags` * Like "group by tags" except that tasks with no tags have no heading instead of "(No tags)". * `group by function task.tags.join(", ")` * Tasks with multiple tags are listed once, with a heading that combines all the tags. * Separating with commas means the tags are clickable in the headings. * `group by function task.tags.sort().join(", ")` * As above, but sorting the tags first ensures that the final headings are independent of order of tags in the tasks. * `group by function task.tags.filter( (tag) => tag.includes("#context/") )` * Only create headings for tags that contain "#context/". * `group by function task.tags.filter( (tag) => ! tag.includes("#tag") )` * Create headings for all tags that do not contain "#tag". These are more complicated examples, which you might like to copy if you use tasks with [nested tags](https://help.obsidian.md/Editing+and+formatting/Tags#Nested+tags) and wish to group them at different tag nesting levels. * `group by function task.tags.map( (tag) => tag.split('/')[0].replace('#', '') )` * `#tag/subtag/sub-sub-tag` gives **`tag`**. * `group by function task.tags.map( (tag) => tag.split('/')[1] ? tag.split('/').slice(1, 2) : '')` * `#tag/subtag/sub-sub-tag` gives **`subtag`**. * `group by function task.tags.map( (tag) => tag.split('/')[2] ? tag.split('/').slice(2, 3) : '')` * `#tag/subtag/sub-sub-tag` gives **`sub-sub-tag`**. * `group by function task.tags.map( (tag) => tag.split('/')[3] ? tag.split('/').slice(3, 4) : '')` * `#tag/subtag/sub-sub-tag` gives no heading, as there is no value at the 4th level. * `group by function task.tags.map( (tag) => tag.split('/')[0] )` * `#tag/subtag/sub-sub-tag` gives **`#tag`**. * `group by function task.tags.map( (tag) => tag.split('/')[1] ? tag.split('/').slice(0, 2).join('/') : '')` * `#tag/subtag/sub-sub-tag` gives **`#tag/subtag`**. * `group by function task.tags.map( (tag) => tag.split('/')[2] ? tag.split('/').slice(0, 3).join('/') : '')` * `#tag/subtag/sub-sub-tag` gives **`#tag/subtag/sub-sub-tag`**. * `group by function task.tags.map( (tag) => tag.split('/')[3] ? tag.split('/').slice(0, 4).join('/') : '')` * `#tag/subtag/sub-sub-tag` gives no heading, as there is no value at the 4th level. ### Original Markdown There is no built-in instruction to group by the original markdown line. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by original markdown line** is now possible. For example, this could be used to extract information from `task.originalMarkdown` that Tasks does not parse, to use for grouping tasks. * ``` group by function '``' + task.originalMarkdown + '``' ``` * Group by the raw text of the task's original line in the MarkDown file as code. * Note the pairs of backtick characters ('\`'), to preserve even single backtick characters in the task line. * It's important to prevent the task checkbox (for example, '\[ \]') from being rendered in the heading, as it gets very confusing if there are checkboxes on both headings and tasks. * `group by function task.originalMarkdown.replace(/^[^\[\]]+\[.\] */, '')` * An alternative to formatting the markdown line as code is to remove everything up to the end of the checkbox. * Then render the rest of the task line as normal markdown. ## Group by File Properties ### File Path * `group by path` (the path to the file that contains the task, that is, the folder and the filename) Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by file path** is now possible. * `group by function task.file.path` * Like 'group by path' but includes the file extension. ### Root * `group by root` (the top-level folder of the file that contains the task, that is, the first directory in the path, which will be `/` for files in root of the vault) `root` grouping option was introduced in Tasks 1.11.0. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by root folder** is now possible. * `group by function task.file.root` * Same as 'group by root'. ### Folder * `group by folder` (the folder to the file that contains the task, which always ends in `/` and will be exactly `/` for files in root of the vault) Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by folder** is now possible. * `group by function task.file.folder` * Same as 'group by folder'. * `group by function task.file.folder.slice(0, -1).split('/').pop() + '/'` * Group by the immediate parent folder of the file containing task. * Here's how it works: * '.slice(0, -1)' removes the trailing slash ('/') from the original folder. * '.split('/')' divides the remaining path up in to an array of folder names. * '.pop()' returns the last folder name, that is, the parent of the file containing the task. * Then the trailing slash is added back, to ensure we do not get an empty string for files in the top level of the vault. ### File Name * `group by filename` (the link to the file that contains the task, without the `.md` extension) * Note that tasks from different notes with the same file name will be grouped together in the same group. Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by file name** is now possible. * `group by function task.file.filename` * Like 'group by filename' but does not link to the file. * `group by function '[[' + task.file.filename.replace('.md', '') + ( task.hasHeading ? ('#' + task.heading) : '') + ']]'` * Like 'group by backlink' but links to the heading in the file. ### Backlink * `group by backlink` (the text that would be shown in the task's [backlink](https://publish.obsidian.md/tasks/Queries/Backlinks), combining the task's file name and heading, but with no link added) ### Heading * `group by heading` (the heading preceding the task, or `(No heading)` if there are no headings in the file) Since Tasks 4.0.0, **[custom grouping](https://publish.obsidian.md/tasks/Scripting/Custom+Grouping) by heading** is now possible. * `group by function (task.heading + '.md' === task.file.filename) ? '' : task.heading` * Group by heading, but only if the heading differs from the file name. * This works well immediately after a 'group by filename' line. * Note the three equals signs '===': these are important for safety in JavaScript. ## Multiple groups You can add multiple `group by` query options, each on an extra line. This will create nested groups. The first group has the highest priority. Each subsequent `group by` will generate a new heading-level within the existing grouping: * First `group by` is displayed as `h4` headings * Second `group by` is displayed as `h5` headings * Third and subsequent `group by` are displayed as `h6` headings Headings are displayed in case-sensitive alphabetical order, not the original order. ## Refining groups ### Reversing groups Reversing of group headings was introduced in Tasks 3.7.0. After the name of the property that you want to group by, you can add the `reverse` keyword. If given, the group headings will be reversed for that property. For example: * `group by due` will sort the group headings: * from **oldest** due date first... * to **newest** due date last * `group by due reverse` will sort the group headings: * from **newest** due date first... * to **oldest** due date last The `reverse` keyword controls the order that group headings are displayed. The [sort by](https://publish.obsidian.md/tasks/Queries/Sorting) facility, by contrast, controls the order in which displays are displayed _inside_ each group. ### Limiting group size You can limit the number of tasks in each group, perhaps to work on the most important things first. ## Notes The order of operations ensures that grouping does not modify which tasks are displayed, for example when the `limit` options are used: 1. all the filter instructions are run 2. then any sorting instructions are run 3. then any `limit` instructions are run 4. then any grouping instructions are run 5. then any `limit groups` instructions are run ## Screenshots ### Before Here is an example Tasks result, without any `group by` commands: ![Tasks Ungrouped](https://proxy-prod.omnivore-image-cache.app/0x0,sSjXUpAFImYOXi0dbzLz2Y9ARB0qM_TlDAVdfIFSbwTw/https://publish-01.obsidian.md/access/40e62a316a834ff6f495ebf1d122cae6/images/tasks_ungrouped.png) Tasks not grouped. ### After And here is what this might look like, when grouped by folder, filename and heading: ![Tasks Grouped](https://proxy-prod.omnivore-image-cache.app/0x0,s4L0CpwKfD_GP_wuUO65IooyumqPfjG2hor7hPbPcUdE/https://publish-01.obsidian.md/access/40e62a316a834ff6f495ebf1d122cae6/images/tasks_grouped.png) Tasks grouped. ## Examples Give me three levels of grouping, to indicate the locations of my tasks: ```routeros ```tasks not done group by folder group by filename group by heading ``` ``` Show me tasks I need to do today - and put today's tasks first, for visibility: ```routeros ```tasks not done due before tomorrow group by due reverse ``` ``` Grouping