Dataview: List internal links by header, with one row per link per header

What I’m trying to do

I’m trying to create a Dataview table that lists each internal link (e.g., [[keyword]]) once per header, per file.

For example, say I have a note at Folder/to/note/note1.md with:

# Header 1
Random [[keyword]] with clear purpose.

# Header 2
Another [[keyword]] with equally clear purpose.

And another note note2.md in the same folder also contains the same [[keyword]] inside one of its headers.

I want to generate a table (in a separate note, located at Folder/to/note/keywords/keyword.md) that shows each file and header that contains [[keyword]], with a link to the header, like:

So

  • Multiple matches from a single note are okay (as long as they’re under different headers).
  • I want a row for each header where the internal link appears.
  • The table should link to that specific header, not just the note.

Things I have tried

I have searched everywhere for a solution to no avail . I’ve tried many codes but the only one that gets me anywhere that isn’t even close is

TABLE 
FROM "Folder/to/note" AND [[keyword]] AND !"Folder/to/note/keywords"

But this only returns the file once if it contains the link, and not for each header where the link appears.

Unless you switch to dataviewjs you can’t do that. There isn’t any information attached to the link to display heading information.

Heading/section information is only available when you’re looking at list/task items.

Hey holroy, I stumbled upon many of your posts in my search. Did try mana dataviewjs code block approaches as well, but having basically no javacode knowledge is making me scratch my already balded head. ChatGPT also couldn’t help me

For those looking for something similar, this works for me:

(async () => {
  const synonyms = new Set(["mängd", "mängder", "mängden", "universala mängden", "universalmängden", "universumsmängden"]);

  const pages = dv.pages(
    '"Årskurs 2/Läsperiod 4/Sannolikhetsteori och statistisk signalbehandling/Föreläsningar"'
  );

  const hits = [];
  for (const p of pages) {
    const text  = await dv.io.load(p.file.path);
    const lines = text.split(/\r?\n/);
    let section = "";

    for (const line of lines) {
      const headMatch = line.match(/^\s*#{1,6}\s+(.*)$/);
      if (headMatch) {
        section = headMatch[1].trim();
        continue;
      }
      const linkRe = /\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|[^\]]+)?\]\]/g;
      let m;
      while ((m = linkRe.exec(line)) !== null) {
        const rawTarget = m[1].trim();
        const target    = rawTarget.toLowerCase();
        if (synonyms.has(target)) {
          hits.push({
            file:    p.file,
            section,
            term:    rawTarget
          });
        }
      }
    }
  }

  const unique = Array.from(
    hits.reduce((map, h) => {
      const key = `${h.file.path}#${h.section}#${h.term}`;
      if (!map.has(key)) map.set(key, h);
      return map;
    }, new Map()).values()
  );

    dv.table(
	    ["Föreläsning", "Rubrik", "Term"],
		unique.map(h => [
	    h.file.link,
	    `[[${h.file.path}#${h.section}|${h.section}]]`,
	    h.term.toLowerCase()
			])
		);
})();

which gives