DataviewJS Snippet Showcase

Well, instead of k.URL you could use something like

"Link".link(k.URL)

where "Link" could even come from a variable or calculation, enclosed in parentheses, like

(k.author).link(k.URL)

effectively making the author clickable and saving a column.

I use this technique in my Show family members, friends and places that are nearby example above.

Note: In olden times, .link wasn’t supported too well in each and every browser. Fortunately, Electron (and thus Obsidian) handles it well.

2 Likes

Thank you so much! This will save unnecessary horizontal scrolling to view all the rows.

Why does the YAML have both these?

Because I’m currently testing and it’s much easier just to comment/uncomment things. :wink:
I also wanted to show how a tag search and a folder search would look like.

Oh I figured it after posting this! I was getting some weird error and was eliminating all possible options that might cause it. Thanks for sharing the code.

Do you happen to know if we can use DataviewJS inline like we could Dataview queries? I’ve been using Templater to have inline “Next Birthday” in individual People note but Templater dynamic templates break all table formatting right now.

List(s) of Obsidian Commands

EDIT: A newer and better version is down in post #37!

I needed a list of currently enabled Obsidian Commands (those in the Command Palette, internal plus plugins). So I thought »Why not do it using Obsidian itself (and Dataview)?«

Commands sorted by internal Command ID


const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

function getHotkey(arr) {
    return arr.hotkeys ? [[getNestedObject(arr.hotkeys, [0, 'modifiers'])],
    [getNestedObject(arr.hotkeys, [0, 'key'])]].flat(2).join('+').replace('Mod', 'Ctrl') : '–';
}

let cmds = dv.array(Object.entries(app.commands.commands))
    .sort(v => v[1].id, 'asc');

dv.paragraph(cmds.length + " commands currently enabled.<br><br>");

dv.table(["Command ID", "Name in current locale", "Hotkeys"],
  cmds.map(v => [
    v[1].id,
    v[1].name,
    getHotkey(v[1]),
    ])
  );
 

Result:

Commands sorted by assigned hotkey

This I missed the most. Just to check if a planned hotkey combo is already taken …


const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

function getHotkey(arr) {
    return arr.hotkeys ? [[getNestedObject(arr.hotkeys, [0, 'modifiers'])],
    [getNestedObject(arr.hotkeys, [0, 'key'])]].flat(2).join('+').replace('Mod', 'Ctrl') : '–';
}

let cmds = dv.array(Object.entries(app.commands.commands))
    .where(v => getHotkey(v[1]) != '–')
    .sort(v => v[1].id, 'asc')
    .sort(v => getHotkey(v[1]), 'asc');

dv.paragraph(cmds.length + " commands with assigned hotkeys.<br><br>");

dv.table(["Command ID", "Name in current locale", "Hotkeys"],
  cmds.map(v => [
    v[1].id,
    v[1].name,
    getHotkey(v[1]),
    ])
  );
 

Result:

Notes

I’m on Linux and this is good enough for me. I leave remapping MacOS keys and/or adding nice <kbd>…</kbd> tags around the keys (but not the plus signs in between!) as an exercise for the reader. :wink:

Anyway, it’s a good check what we have, and could well be a starting point for the Buttons and Obsidian Leaflet plugins to maybe invoke commands language-independently, using their ID. Because now we know it an can make a list!

(Bi- and multilingual people switch Obsidian’s language often, and it sucks that commands called up by their (locale-specific!) name suddenly stop working.)

10 Likes

This is a very thoughtful snippet from @Moonbase59 like always! I’ve added some symbols in this for the Mac Shortcuts,

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

function getHotkey(arr) {
    return arr.hotkeys ? [[getNestedObject(arr.hotkeys, [0, 'modifiers'])],
    [getNestedObject(arr.hotkeys, [0, 'key'])]].flat(2)
	.join(' ')
	.replace('Mod', '⌘')
	.replace('Shift', '⇧')
	.replace('Alt', '⌥')
	.replace('Ctrl', '⌃')
	.replace('ArrowRight', '→')
	.replace('ArrowLeft', '←')
	.replace('ArrowUp', '↑')
	.replace('ArrowDown', '↓')
	.replace('Enter', '⏎')
	.replace('Tab', '⇥'): '–';
}

let cmds = dv.array(Object.entries(app.commands.commands))
    .where(v => getHotkey(v[1]) != '–')
    .sort(v => v[1].name, 'asc')
    .sort(v => getHotkey(v[1]), 'asc');

dv.paragraph(cmds.length + " commands with assigned hotkeys.<br><br>");

dv.table(["Command ID", "Name in current locale", "Hotkeys"],
  cmds.map(v => [
    v[1].id,
    v[1].name,
    getHotkey(v[1]),
    ])
  );

Although it works fine for me right now, except for sorting which needs some fixing, I’m sure I’m missing other symbols people might use,

This is a good place to find some of the common symbols if the built-in character viewer seems too much.

7 Likes

Hi I built a quick script with dataviewjs to collate my daily note lists for my weekly note. It’s using regular expressions and the obsidian API to accomplish this. I’m pretty new to using javascript so any feedback will be appreciated.

//Set the beginning and end of the week
const beg = moment("2021-05-16")
const end = moment("2021-05-22")

//The RegEx that does the heavy lifting
const er = new RegExp(/##.Lists\n([\s|\S]*)##.[I|D]/g);
const re = new RegExp(/(?:####.*\n)(?:[-|\d.].*\n)*/g);

/*
My Lists look like this
## Lists
#### a list
- markdown
#### another list
- [ ] it catches these too
*/

//Obsidian's API has allows you to grab a file's parent
const par = app.vault.getAbstractFileByPath("Daily/2021-05-13.md").parent;

//Get the files for the week as a Map 
const week = par.children.reduce((acc,chil) => {
	let name = chil.basename;
	let date = moment(name);
	if (date >= beg && date <= end){
		acc.set(name,chil)	
	}
	return acc
},new Map())

const sortWeek = new Map([...week].sort((a,b) => String(a[0]).localeCompare(b[0])))

sortWeek.forEach((v,k,m) => {
	let path = app.vault.getAbstractFileByPath(v.path)
	app.vault.read(path)
	.then( fileText => {
		let listText = fileText.match(er)
		return new Promise((resolve,reject) =>{resolve(...listText)})
	})
	.then(listText => {
		const lists = listText.match(re);
		if (lists !== null){
			dv.header(4,k)
			dv.list(lists)
		}
	})
});

4 Likes

Sortable columns—Note to myself

I’m using the really helpful Sortable Plugin, so columns in my tables can be easily sorted in preview mode. It it in its early stages, but already quite practical to use.

Of course it won’t be able to sort columns with “odd” contents, like user-formatted dates, for instance.

Nevertheless, since we could write a sort comparator function in Javascript for these, within the code block, it should be a snap if the author of Sortable could provide us with a hook to add our own comparator functions per column. And if we could access this within the “Dataview Javascript”.

I opened a feature request for this on Sortable’s GitHub page. Maybe you’d like to support it.

Related:

3 Likes

I’m looking to create a dataview table that I think can only be accomplished with Dataviewjs.

I have Literature Notes that look like this:

I would like to show ALL Literature Notes in a table sorted descending by their “secondary tag” frequency. The secondary tags are all tags that are not “#litnote”. In this example the only secondary tag is “#writing” but some notes have more.

This is so I can find clusters of “Literature Notes” that belong to the same tag. I am at a loss at the moment to figure out how to associate the tags with the notes. I can grab the tags themselves and the notes themselves in js, but not the correlation between the two.

I hope this is clear. Thank you in advance for any help.

@Azulaloi was looking for a dataview snippet that would give the word count of all pages in a vault with a specific tag. Uses the guts of the Better Word Count Plugin by @lukeleppan. Works really well with MetaEdit to change the search term. Here is what I came up with: dataviewjs.wordcountfromtag · GitHub

5 Likes

Thanks for this!, one question.

Where I can know more about those ‘inline metadata fields’? they look simple and amazing :slight_smile:

Here’s another thread that shows how an idea evolves and DataviewJS is used: @FiekeB and I were talking about how a recipe data collection could be set up.

2 Likes

REAL Daily Notes previous/next navigation

Sometimes I have large gaps between my Daily Notes, but still like the idea of having a “previous Daily Note / next Daily Note” navigator at the top.

Now this doesn’t work without actually traversing the file tree, and what’s better for that than DataviewJS?

I’m a little hesitant of putting such an amount of code into every Daily Note, so we might have to wait for common includes. Nevertheless, here is the code for those who wish to play around with it. It’s quite robust (doesn’t break when there is no previous/today’s/next note, and also works for notes with names different from YYYY-MM-DD).

It’s also great to use in a folder with weekly or monthly notes, because it “travels” through all files in “this” folder (and its subfolders) that have either

  • a Dataview-recognizable date in their name, or
  • a date: (ISO format) field in their YAML.

If no note with today’s date can be found, it’ll display a “date hint” in parentheses in the middle, plus the previous and next notes links (based on today’s date). The “date hint” is shown in the format set in the Daily Notes plugin’s settings, or ‘YYYY-MM-DD’ if the plugin is disabled or no date format has been set.

Code

```dataviewjs
/*
    previous/next note by date for Daily Notes
    Also works for other files having a `date:` YAML entry.
    MCH 2021-06-14
*/
var none = '(none)';
var p = dv.pages('"' + dv.current().file.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() : luxon.DateTime.now().toISODate();
// Obsidian uses moment.js; Luxon’s format strings differ!
var format = app['internalPlugins']['plugins']['daily-notes']['instance']['options']['format'] || 'YYYY-MM-DD';
var current = '(' + moment(t).format(format) + ')';
var nav = [];
var today = p.find(p => p[1] == t);
var next = p.find(p => p[1] > t);
var prev = undefined;
p.forEach(function (p, i) {
    if (p[1] < t) {
        prev = p;
    }
});
nav.push(prev ? '[[' + prev[0] + ']]' : none);
//nav.push(today ? today[0] : none);
nav.push(today ? today[0] : current);
nav.push(next ? '[[' + next[0] + ']]' : none);

//dv.list(nav);
//dv.paragraph(nav.join(" · "));
dv.paragraph(nav[0] + ' ← ' + nav[1] + ' → ' + nav[2]);
```

I’ve left in some commented-out other ways for displaying. You can of course also leave out today’s note in the middle.

Suggestions for optimizing the JS without breaking functionality welcome.

Screenshots

Auswahl_015

Auswahl_016

Auswahl_017

Changelog:

  • 2016-06-14
    • initial version
  • 2016-06-14
    • If it’s unable to pick up a note name for today, will now show a hint at “where” we are in the middle, based on the day format set in the Daily Notes plugin settings.

      Auswahl_018
      Standard date format “YYYY-MM-DD”

      Auswahl_019
      Unusual date format “ww’ DD-MMM (ddd) Y” used by @den

14 Likes

Hi - I’m trying to have dataviewjs show me any open Kanban tasks that are due today or overdue. It’s beyond my javascript abilities… does anyone have a suggestion on how to change this code to check for any incomplete tasks that are undated or are <= 2021-06-14 ?

dv.taskList(dv.pages("[[2021-06-14]]").file.tasks
	.where (t => !t.completed) 
	.where (t => t.text.includes("2021-06-14")))

Here’s a code block that finds any tasks which include a date of the form YYYY-MM-DD and are overdue (i.e. have a date earlier than now). Yes my code is verbose :slight_smile:

function overdue(t) {
  let dValidate = moment(t.text, 'YYYY-MM-DD', true);
  let d = moment(t.text, 'YYYY-MM-DD');
  let containsValidDate = dValidate._pf.unusedTokens.length==0 ;
  let isOverdue = d.diff(moment()) <= 0;
  return (containsValidDate && isOverdue);
}

dv.taskList(dv.pages("").file.tasks
	.where (t => overdue(t))
	.where (t => !t.completed))

Hi! Im trying to have a daview table in my meeting note with the last 3 meetings from the date of the meeting I’m taking notes…can anybody help me please…Thanks in advance

1 Like

HI! really nice! im having hard times figuring out what to chance in here for tasks unscheduled and coming in the future…can you advice? Thanks!!!

Here you go. If you just want upcoming, then use the commented-out return statement instead

function filter(t) {
  let dValidate = moment(t.text, 'YYYY-MM-DD', true);
  let d = moment(t.text, 'YYYY-MM-DD');
  let unscheduled = !dValidate._pf.unusedTokens.length==0 ;
  let upcoming = (!unscheduled) && moment().diff(d) < 0;
  return (unscheduled || upcoming)
  // return ( upcoming );
}

dv.taskList(dv.pages("").file.tasks
	.where (t => filter(t))
	.where (t => !t.completed))
1 Like

Hey, it may just be that I don’t have access to the repo but that link is giving me a 404. Any chance you have a better one?

1 Like