DataviewJS Snippet Showcase

Hey @Moonbase59 ! Thanks a lot for sharing this code. It has been conducive to me.
I had to make a minor change to remove the Luxon dependency, which was breaking for me.
Also, as I use a different folder structure for my daily notes, I added some code to search the pages from the daily notes configured folder. The relevant lines of code I changed:

var folder = app['internalPlugins']['plugins']['daily-notes']['instance']['options']['folder'] || dv.current().file.folder
var p = dv.pages('"'+folder+'"').where(p => p.file.day).map(p => [p.file.name, p.file.day.toISODate()]).sort(p => p[1]);
var t = dv.current().file.day ? dv.current().file.day.toISODate() : dv.date("now").toFormat("yyyy-MM-dd");

Cheers,
Gilmar

Please keep posts in this thread for “share and showcase”, and ask questions in Help

Noted with thanks.

Wanted a drop down menu for my dashboard so I used chat gpt to create this query and a css snippet to style it. I’m sure the query could be simplified or there is a better way to do this but it works and links to the pages AND it’s all updated automatically.

function formatDate(dateString) {
  let date = new Date(dateString);
  let options = { year: "2-digit", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: true };
  return date.toLocaleString("en-US", options);
}

let folderTree = new Map();

function addToFolderTree(path, content) {
  let parts = path.split("/");
  let currentLevel = folderTree;
  for (let i = 0; i < parts.length; i++) {
    let part = parts[i];
    if (!currentLevel.has(part)) {
      currentLevel.set(part, { folders: new Map(), files: "" });
    }
    if (i === parts.length - 1) {
      currentLevel.get(part).files = content;
    } else {
      currentLevel = currentLevel.get(part).folders;
    }
  }
}

for (let group of dv.pages('"----01. PROJECTS"').groupBy(p => p.file.folder)) {
  let folderName = group.key;
  let rows = group.rows
    .map(
      k => `| [${k.file.name.replace(/\|/g, "\\|")}](/${k.file.path.replace(/ /g, "%20")}) | ${formatDate(k.file.ctime)} | ${formatDate(k.file.mtime)} |`
    )
    .join("\n");

  let tableContent = `
| Name | Created | Modified |
| ---- | ------- | -------- |
${rows}
`;

  addToFolderTree(folderName, tableContent);
}

function renderFolderTree(folderMap, level = 0) {
  let content = "";
  for (let [folder, data] of folderMap.entries()) {
    let subcontent = data.folders.size > 0 ? renderFolderTree(data.folders, level + 1) : "";
    let folderContent = data.files ? data.files : "";
    if (level > 0) {
      content += `### ${folder}\n<details><summary>Click to expand</summary>\n\n${subcontent}${folderContent}\n\n</details>\n`;
    } else {
      content += `${subcontent}${folderContent}`;
    }
  }
  return content;
}

dv.header(6, renderFolderTree(folderTree));

1 Like

Hello!

There’s an error somewhere in this piece of code:

let pages = dv.pages("#Q_A").where(p => p.file.outlinks = "[[consultation]]")

or

let pages = dv.pages("#Q_A").where(p => p.file.outlinks.includes("[[consultation]]")

I don’t understand how to write it correctly to make it work.

Whole code:

```dataviewjs
let pages = dv.pages("#Q_A").where(p => p.file.outlinks === "[[консультация]]")


for (let group of pages.groupBy(p => p.status)) {
    dv.header(3, group.key);
    dv.table(["Column1", "Column2"],
        group.rows  
          .map(k => [k.file.link, k["created"]]
))
}

Help me, please.

Thank you so much!


Updated: In discord helped River :+1::

dv.pages("[[consultation]]")
  .where(p => p.file.tags.includes("#Q_A"))
1 Like

SEARCH FOR AND DISPLAY LIST OF CALLOUTS (WITH LINKS TO NOTES) IN A SPECIFIED FOLDER WITH A SPECIFIED TAG:

I scrolled through a fair amount of this thread looking for a way to list ‘todo’ callouts with a link, but only in a specific folder with a specified tag. I could not find it, so I am posting this for posterity’s sake. Credit to this question thread which allowed me to form a base to build this.

```dataviewjs
// Replace TAG with whatever tag you wish.
const pages = dv.pages('#TAG')

//Use the regular expression to define what kind of Callout you want to check
//Replace !todo with whatever callout type you wish.
const regex = />\s\[\!todo\]\s(.+?)((\n>\s.*?)*)\n/

//check for more types of callouts by adding new const regex# where # is a number
const regex2 = />\s\[\!TODO\]\s(.+?)((\n>\s.*?)*)\n/

const rows = []
for (const page of pages) {
    const file = app.vault.getAbstractFileByPath(page.file.path);
    //get file path as string
    var checkMe = "" + page.file.path;
    // Read the file contents
    const contents = await app.vault.read(file)
    // Extract the summary via regex
	for (const callout of contents.match(new RegExp(regex, 'sg')) || []) {

        //check if the file path is in your designated file path.
        //change FILEPATH to any keyword, folder name, or file path
        //You can also change to exclude a file path by changing "==true" to "!=true"
	    if(checkMe.includes('FILEPATH') == true){
		    const match = callout.match(new RegExp(regex, 's')) 
		    rows.push([match[1], page.file.link])
		}
    }

     //duplicate the for loop for each const regex# you created
     //adjust regex to the appropriate name
    for (const callout of contents.match(new RegExp(regex2, 'sg')) || []) {
	    if(checkMe.includes('FILEPATH') == true){
		    const match = callout.match(new RegExp(regex2, 's')) 
		    rows.push([match[1], page.file.link])
		}
    }
}

dv.table(['Term', 'Link'], rows)
```

I am still playing with this, and if I update it, I will edit this post or reply to it.
Hope this helps someone.

SEARCH FOR AND DISPLAY LIST OF CALLOUTS (WITH LINKS TO NOTES) IN A SPECIFIED FOLDER WITH A SPECIFIED TAG (UPDATE):
I went and tried to edit the post I made to include the regex Array, but I was unable to do so.

```dataviewjs
// Replace TAG with whatever tag you wish.
const pages = dv.pages('#TAG')

//Use the regular expression to define what kind of Callout you want to check
//Replace !todo with whatever callout type you wish.
//check for more types of callouts by adding new entries in the array.
//Remember: every key in the Array has to have a comma at the end, except for the last one
const regex = {
	0: />\s\[\!todo\]\s(.+?)((\n>\s.*?)*)\n/,
	1: />\s\[\!TODO\]\s(.+?)((\n>\s.*?)*)\n/
}
const rows = []
for (const page of pages) {
    const file = app.vault.getAbstractFileByPath(page.file.path);
    //get file path as string
    var checkMe = "" + page.file.path;
    // Read the file contents
    const contents = await app.vault.read(file)
    //cycle through the callout type array. 
    //Be sure to change the 'i<2' to be one more than your highest array key.
    for(var i = 0; i < 2; i++){
        // Extract the summary via regex
		for (const callout of contents.match(new RegExp(regex[i], 'sg')) || []) {
            //check if the file path is in your designated file path.
            //change FILEPATH to any keyword, folder name, or file path
            //You can also change to exclude a file path by changing "==true" to "!=true"
		    if(checkMe.includes('FILEPATH') == true){
			    const match = callout.match(new RegExp(regex[i], 's')) 
			    rows.push([match[1], page.file.link])
			}
	    }
	}
}

dv.table(['Term', 'Link'], rows);
```

How do you use this?

This is great and I would love to implement this, however I can’t get it to work.

In the People note I have added this frontmatter:
tags: [family, friends]
birthday: 2021-08-17

In the Birthdays note I have this frontmatter:
#searchterm:#family or #friends
searchterm: ‘“People”’
duration: 1 year
dateformat: “YYYY-MM-DD”

In this note I have placed the dataviewjs code.
It displays a Dataview query with no results.

Any help would be greatly appreciated.

Not the OP, but the following works in a local diagnostic (see screenshot below).

Person File:

---
birthday: 2021-08-17
tags: family
---

Birthdays File

---
searchterm: '#family'
duration: 1 year
---

```dataviewjs
var start = moment().startOf('day');
var end = moment(start).add(dv.current().duration);
var dateformat = "YYYY-MM-DD";
if (dv.current().dateformat) { dateformat = dv.current().dateformat; }

// info text above table, {0}=duration, {1}=start date, {2}=end date
// parameters can be left out, or the string empty
var infotext = "Upcoming birthdays for {0} from now ({1} – {2})<br><br>";

//=============

function nextBirthday(birthday) {
    // Get person’s next birthday on or after "start"
    // returns a moment
    
    // need to "unparse" because DV has already converted YAML birthday to DateTime object
    // shouldn’t harm if already a string
    var bday = moment(birthday.toString());
    var bdayNext = moment(bday).year(start.year());
    if (bdayNext.isBefore(start, 'day')) {
        bdayNext.add(1, "year");
    }
    return bdayNext;
}

function turns(birthday) {
    // Get the age in years a person will turn to on their next birthday

    // need to "unparse" because DV has already converted YAML birthday to DateTime object
    // shouldn’t harm if already a string
    var bday = moment(birthday.toString());
    return nextBirthday(birthday).diff(bday, 'years');
}

function showBirthday(birthday) {
    // Determine if this birthday is in the range to be shown
    // including the start date, excluding the end date
    // because that comes from a duration calculation
    // for use with "where", returns true or false
    
    if (birthday) {
        // need to "unparse" because DV has already converted YAML birthday to DateTime object
        // shouldn’t harm if already a string
        var bday = moment(birthday.toString());
        var bdayNext = nextBirthday(birthday);
        if (bdayNext.isBetween(start, end, 'day', '[)')) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

function sortByNextBirthday(a, b) {
    // comparator function for "sort"
    
  if (nextBirthday(a).isBefore(nextBirthday(b))) {
    return -1;
  }
  if (nextBirthday(a).isAfter(nextBirthday(b))) {
    return 1;
  }
  // they’re equal
  return 0;
}


//======================================================================

dv.paragraph(infotext.format(moment.duration(dv.current().duration.toString()).humanize(), start.format(dateformat), end.format(dateformat)));

dv.table(
    ["Name", "Birthday", "Turns"],
    dv.pages(dv.current().searchterm)
        // use a function to see if this birthday is in range to be shown
        .where(p => showBirthday(p.birthday))
        // use a comparator function to sort by next birthday
        .sort(p => p.birthday, 'asc', sortByNextBirthday)
        .map(p => [
            p.file.link,
            p.birthday ? nextBirthday(p.birthday).format(dateformat) : '–',
            turns(p.birthday)
        ])
);
```

1 Like

Thank you so much! I must have misunderstood how to add the frontmatter to the person’s note. How can I query note’s tagged family or friend?

Any idea how to query friends or family with birthday month and day same as today? Would like to show today’s birthdays in a daily note.

how can you change the duration?