Avoid grouping by note name in dataviewjs query

Things I have tried

For this example, I have two notes (Note 1 and Note 2), and each contain two tasks, with a link to the daily note, and a timestamp at the beginning of the task content

Note 1

  • [ ] 9:00 wake up [2023-01-25]
  • [ ] 10:00 Read [2023-01-25]

AND

Note 2

  • [ ] 9:30 brush teeth [2023-01-25]
  • [ ] 11:00 start work [2023-01-25]

I am writing a dataviewjs query that gets all tasks in my vault that have a date, and orders them using the time stamp at the beginning of the task.

This is what I have done so far:

dv.taskList(
	dv.pages().file.tasks 
	  .where(t => !t.completed)
	  .where(t => t.text.includes("2023-01-25"))
	  .sort(t => Date.parse(t.text.replace(/ .*/,'')), 'asc')
)

The problem is that it is ordering the tasks but still showing themas a part of the note they come from:

image

(In case the picture does not show:)

Note 1

  • [ ] 9:00 wake up [2023-01-25]
  • [ ] 10:00 Read [2023-01-25]

Note 2

  • [ ] 9:30 brush teeth [2023-01-25]
  • [ ] 11:00 start work [2023-01-25]

What I’m trying to do

Instead, I want them to be ordered by the timestamp, regardless of the note they are coming from. Expected result would look like this

image

(In case the picture does not show:)

  • [ ] 9:00 wake up [2023-01-25]
  • [ ] 9:30 brush teeth [2023-01-25]
  • [ ] 10:00 Read [2023-01-25]
  • [ ] 11:00 start work [2023-01-25]

There some things to consider in this query, but the main thing causing the result to group together is the missing second parameter to dv.taskList(..., false). See Codeblock Reference - Dataview

However, let us also address some other issues whilst doing this response.

Hard-coded dates as text?!

I’m hoping this is just for the test purposes, and that you don’t intend to hard-code your dates like in the following line:

  .where(t => t.text.includes("2023-01-25"))`

And that given the query resides in a daily note, you’d rather use something using the link as a link, and fetching tasks just linking to the current file.

The sort line

You use the following for sorting purposes:

.sort(t => Date.parse(t.text.replace(/ .*/,'')), 'asc')

Here you replace everything after the space with nothing, so your basically left with the timestamp, which you then try to convert to a date. But a timestamp is not a date, unless it actually gets something like 2023-01-25T in front of it.

And even then, you would have an issue related to timestamps with just single digits as hours. E.g. “2023-01-25T9:00” is not a legal date and timestamp, so it wouldn’t be parsed as such. In other words, your sorting as it stands just sorts on an undefined value, a non-legal date.

To correct this, you could either add in a (random) date in front of the text, like I did here: How to calculate amount of time in dataview query - #4 by holroy

Or, you could also opt for adding the date of the link into it, to make it more “real”. Still, you’d want to tackle the issue with single digits in the hour place, so you could do something like: t.text.replace(/^([0-9]:)/, "0$1") before the concatenation with the date.

Skip the date parsing, and just sort on the text

I don’t really see the need for doing that date parsing, unless you plan to include multiple dates into the mix, so a simpler solution would be the following:

```dataviewjs
dv.taskList(
  dv.pages().file.tasks 
    .where(t => !t.completed)
	.where(t => t.text.includes("2023-01-25"))
	.sort(t => t.text
	  .replace(/^ */, "")
	  .replace(/^([0-9]:)/, "0$1")),
  false)
```

Here I’ve not compensated for the hard-coded date in the first where clause, but I’ve adjusted the sorting. Firstly I just remove any extraneous spaces at the beginning, to level out the playing field if you were sloppy with your spaces one day.

Secondly, I add a zero in front of the single digit (when sorting), to make it sort better. I don’t bother removing the rest of the line, as in most cases it wouldn’t matter. However, if you were to have to activities on the same time, they would be sorted alphabetically. Consider that an added bonus… :smiley:

Lastly, I add the , false parameter to dv.taskList() to remove the file grouping.

With my slightly extended test files, I now get this output:

Note 1

- [ ] 9:00 wake up [[2023-01-25]] (1st)
- [ ] 10:00 Read  [[2023-01-25]] (1st)
- [ ] 1:30 Night snack [[2023-01-25]] (1st)

In Note 2

- [ ] 2:00 more night food [[2023-01-25]] (2nd)
- [ ] 9:30 brush teeth [[2023-01-25]] (2nd)
- [ ] 11:00 start work[[2023-01-25]] (2nd)

image

Which we can see sorts properly according to the timestamp, and doesn’t group anything based on the original files (as indicated by the mix of 1st and 2nd at the end).

1 Like

Thank you so much!

I did not plan to use the hard-coded dates, just like you mentioned.
However, I did find the extra details you shared very enlightening, since I am just starting!

In the end, I wrote a query for a bunch of tables that divided the tasks by dates and a table for undated tasks, based on the codes I found in Tasks by Date - for Obsidian and Dataview.js · GitHub

I also added the ability to add habits to the Daily Template. The query takes all tasks in the current note that have the tag #habit and adds them to the list of the day. This makes it easy to change the time a recurrent habit is done, when circumstances arise.

Here’s the code:


// find dates based on format [[YYYY-MM-DD]]

const findDated = (task)=>{
	if( !task.completed ) {
		task.link = " " + "[[" + task.path + "|*]]";
		task.link = 
		task.date="";
		const found = task.text.match(/\[\[([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))\]\]/);
		if(found) task.date = moment(found[1]);
		return true;
	}
}

// Builds the lists of tasks for each header
const myTasks = dv.pages("").file.tasks.where(t => findDated(t))

dv.header(3,"Overdue");

dv.table(["task","link"], myTasks.filter(t=> moment(t.date).isBefore(dv.current().file.name,"day")).sort(t => t.text .replace(/^ */, "") .replace(/^([0-9]:)/, "0$1")).sort(t=>t.date) .map(t=>[t.text, t.section]));

// The tasks for the day take the tasks in the same daily note that contain the #habit tag
dv.header(3,"Today");

dv.table(["task","link"], myTasks.filter(
		t => (moment(t.date).isSame(dv.current().file.name,"day"))
		||
		(t.tags.includes("#habit")
		)
	)
	.sort(t => t.text .replace(/^ */, "") .replace(/^([0-9]:)/, "0$1")) .map(t=>[t.text, t.section]));


dv.header(3,"Upcoming");

dv.table(["task","link"], myTasks.filter(t=> moment(t.date).isAfter(dv.current().file.name,"day")).sort(t => t.text .replace(/^ */, "") .replace(/^([0-9]:)/, "0$1")).sort(t=>t.date).limit(10) .map(t=>[t.text, t.section]));

// This part takes all undated tasks 
dv.header(3,"Undated");

dv.table(["task","link"], myTasks.filter(t=> (!t.date) && !(t.tags.includes("#habit"))).sort(t=>t.text).map(t=>[t.text , t.section]));

This is how it looks like:

Some might argue this could have been done with the “Tasks” plugin but this plug-in does not allow for the tasks to be ordered using the “timestamp”, which is what makes this a day planner instead of a todo list.

I have two questions left:

  1. Is there a way to sort by the “timestamp” when it is not written at the beginning of the block?

For example, if I write

  • [ ] Feed the 13:00 dogs [[date]]
  • [ ] Feed the cats [[date]] 6:00

The sort will make the query result in:

  • [ ] Feed the cats [[date]] 6:00
  • [ ] Feed the 13:00 dogs [[date]]
  1. How do I make a real timestamp from those times extracted by the regex?

Thank you again! I would not have been able to figure out much of this without your help.

The sort() function can do all possible shenanigans if you want it to. It’s just a matter of how you pull out stuff, and which string you return for the sort to work with.

This example is most likely not going to work, but just to show case something:

.sort(t => {
  const timestamp = t.text.match(/[0-2]?[0-9]:[0-5][0-9]/)
  const date = t.text.match(/[12][0-9]{3}-[0-1][0-9]-[0-3][0-9]/)
  return date + "T" + timestamp
})

This should (theoretically) pull the timestamp and date as two seperate entities wherever they’re placed in the string, and use the collated timestamp “YYYY-MM-DD[T]HH:mm” for sorting.

The hour and minutes are not a real timestamp, but if you add them together with a date like I did in the previous snippet, then you could feed that into moment(date + "T" timestamp) and you’d get a proper timestamp out of it. (You could also use dv.func.date(date + "T" + timestamp), if I remember correctly)

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.