Error in user YAML: Alias parsing is not enabled.
---
title: &title "Output a table of notes with a particular heading and the heading's content"
UUID: &UUID 7fa1055a-f5bf-4d85-b3d6-32e4a3c2105b
alias:
- *title
- *UUID
created: 2023-04-26T04:31:24
---
%%
[!metadata]- Dataview fields
- [description:: A
dataviewjs
snippet to search a vault for notes with a given heading in a given path and output a link to those notes in one column with the heading's content in the other. Optionally, output subheadings too which is more resource intensive.]- [directions:: Copy the [[#Code|codeblock]] to your vault and modify the value of
findHeading
to the heading your want to find (without hash#
characters), as well as the value ofrootFolderPath
to the folder you would like to search (leave blank for the root of your vault).]- [type of:: [[Dataview JS snippets π|Dataview JS snippet]]]
%%
$=dv.header(1, jsinit.dvTitleType(dv))
[!abstract]
$=jsinit.dvDescription(dv)
[!metadata]
$=await jsinit.dvView(dv, 'table', {preset: 'meta', files: true})
Code π
~~~dataviewjs
const findHeading = 'Notes',
rootFolderPath = 'path/to/folder',
filesWithHeading = app.metadataCache
.getCachedFiles()
.filter((p) => RegExp(`^${rootFolderPath}`).exec(p))
.filter((p) => app.metadataCache.getCache(p).headings?.map((h) => h.heading === findHeading)),
getPageContentByHeading = async (path, heading, subheadings = false) => {
const tfile = app.vault.getAbstractFileByPath(path),
{ headings } = app.metadataCache.getFileCache(tfile),
foundHeading = headings.find((e) => e.heading === heading);
let content, siblingHeadings, nextHeading, startPos, endPos, headingContent;
if (foundHeading) {
content = await app.vault.cachedRead(tfile);
siblingHeadings = headings.filter((e) => e.level === foundHeading?.level);
nextHeading = subheadings
? siblingHeadings[siblingHeadings.indexOf(foundHeading) + 1]
: headings[headings.indexOf(foundHeading) + 1];
startPos = foundHeading.position.end.offset + 1;
endPos = nextHeading?.position.start.offset - 1;
headingContent = endPos ? content.substring(startPos, endPos).trim() : content.substring(startPos).trim();
const matchImageEmbeds = /!\[\[(?<file>.*?\.(?:png|jpg))(?:\|(?<caption>.*?))?\]\]|\(?<caption>.*?)\)\[(?<file>.*?)\]/g;
let foundImageEmbed;
while ((foundImageEmbed = matchImageEmbeds.exec(headingContent)) !== null) {
headingContent = headingContent.replace(
foundImageEmbed[0],
`<img src="${app.vault.getResourcePath(
app.vault.getFiles().find((f) => f.name === foundImageEmbed.groups.file) ?? {
path: foundImageEmbed.groups.file,
}
)}">${foundImageEmbed.groups.caption ?? ''}</img>`
);
}
}
return headingContent;
},
tableData = (
await Promise.all(
filesWithHeading.map(async (p) => [
dv.page(p).file.link,
await getPageContentByHeading(p, findHeading /* , true */),
])
)
).filter((v) => v[1])
.sort((a, b) => b[0].path.localeCompare(a[0].path, 'en', { sensitivity: 'base' }));
await dv.table(['Path', findHeading], dv.array(tableData));
dv.container.querySelectorAll('tr > td ~ td').forEach((e) => (e.style.whiteSpace = 'normal'));
~~~
const findHeading = 'π₯ Inbox',
rootFolderPath = '2 - Areas πΊοΈ/Periodic Notes π',
filesWithHeading = app.metadataCache
.getCachedFiles()
.filter((p) => RegExp(`^${rootFolderPath}`).exec(p))
.filter((p) => app.metadataCache.getCache(p).headings?.map((h) => h.heading === findHeading)),
getPageContentByHeading = async (path, heading, subheadings = false) => {
const tfile = app.vault.getAbstractFileByPath(path),
{ headings } = app.metadataCache.getFileCache(tfile),
foundHeading = headings.find((e) => e.heading === heading);
let content, siblingHeadings, nextHeading, startPos, endPos, headingContent;
if (foundHeading) {
content = await app.vault.cachedRead(tfile);
siblingHeadings = headings.filter((e) => e.level === foundHeading?.level);
nextHeading = subheadings
? siblingHeadings[siblingHeadings.indexOf(foundHeading) + 1]
: headings[headings.indexOf(foundHeading) + 1];
startPos = foundHeading.position.end.offset + 1;
endPos = nextHeading?.position.start.offset - 1;
headingContent = endPos ? content.substring(startPos, endPos).trim() : content.substring(startPos).trim();
const matchImageEmbeds = /!\[\[(?<file>.*?\.(?:png|jpg))(?:\|(?<caption>.*?))?\]\]/g;
let foundImageEmbed;
while ((foundImageEmbed = matchImageEmbeds.exec(headingContent)) !== null) {
headingContent = headingContent.replace(
foundImageEmbed[0],
`<img src="${app.vault.getResourcePath(
app.vault.getFiles().find((f) => f.name === foundImageEmbed.groups.file) ?? {
path: foundImageEmbed.groups.file,
}
)}">${foundImageEmbed.groups.caption ?? ''}</img>`
);
}
}
return headingContent;
},
tableData = (
await Promise.all(
filesWithHeading.map(async (p) => [
dv.page(p).file.link,
await getPageContentByHeading(p, findHeading, true),
])
)
).filter((v) => v[1])
.sort((a, b) => b[0].path.localeCompare(a[0].path, 'en', { sensitivity: 'base' }));
await dv.table(['Path', findHeading], dv.array(tableData).limit(60));
dv.container.querySelectorAll('tr > td ~ td').forEach((e) => (e.style.whiteSpace = 'normal'));
await jsinit.dvView(dv, 'breadcrumbs', {direction: 'up'})
await jsinit.dvView(dv, 'breadcrumbs', {direction: 'down'})
await jsinit.dvView(dv, 'table', { files: true })
- #βοΈ/fix [[#^41d4dd|embed bugs]] %% fold %%
- [<] [timestamp:: 2023-04-26T07:05:46]
- #πͺ΅/modified the snippet to embed images properly %% fold %%
- [<] [timestamp:: 2023-04-26T06:17:04]
- [i] #π doesn't output all embeds correctly %% fold %% ^41d4dd
- only matches
[[wikilink]]
not[markdown links](markdownn links)
- only matches by filename, which will fail if a link uses a path
- [<] [timestamp:: 2023-04-26T04:33:23]
- only matches
Example