Summary_code
title: DVJS20_List_entire_headings_groupBy_filename_groupIn_Tags_Of_Heading_via_readRaw_of_Obsidian_API =>1.To define a `aoh_rows` (to store key-value pairs of each raw heading) 2.To filter the `aoh_rows` by `h_row.Tags_Of_Heading`(To gather a heading where the `Tags_Of_Heading` is not undefined) 3.To group each `h_row` of the `aoh_rows` by `h_row.Name_Of_File` (Results:)(key=G1.key)(values=G1.rows) 4.To group each `G1.rows` of the `G1` by `G1.Tags_Of_Heading` (Results:)(key=G2.key)(values=G2.rows=G1.rows.rows) {The Bottom Level(Grouped Twice): `G2`=`G1.rows`;` G2.rows`=`page`} 5.To display the result: 5.1 To display each `G1.key`(`Name_Of_File`) as a heading element(H3) 5.2 To display each a `G2.key`(`Tags_Of_Heading`) as a heading element(H6) 5.3 To display each `G2.rows.Link_Of_Heading` as a list 5.4 The H3 is transformed into a link via the dv.fileLink function. 5.5 The H6 is transformed into a link via the dv.sectionLink function.
collapse: open
icon:
color:
```dataviewjs
// M10. define b_display_original_level:
// false: Not to display the original level of each heading (default)
// true : To display the original level of each heading
// Tips: Modify it as needs.
// #####################################################################
let b_display_original_level = false;
// M11. define pages: gather all relevant pages
// Tips: Modify it as needs.
// #####################################################################
let pages = dv
.pages(
`"100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data"`
)
// .where((page) => dv.func.contains(page.file.name, "dic_"))
// .where(
// (page) =>
// page.file.ctime >= dv.date("1967-03-01T00:00:00") &&
// page.file.ctime <= dv.date("2022-12-30T23:59:59")
// )
// .where((page) => dv.func.contains(page.file.tags, "#Project"));
// M12. define b_disable_raw_hyperlink:
// false: Not to disable the raw hyperlink of a raw heading (default)
// Notes: when each heading doesn't contain a link
// Notes: For best performance
//
// true : To disable the raw hyperlink of a raw heading
// Purposes:To disable the raw hyperlink of a raw heading as follows
// R_input:### what_S3: \[\[Note R Q28\]\]
// R_output:### what_S3: 【Note R Q28】
// Q_input:### what_S2: \[[Note Q Q28]\]
// Q_output:### what_S2: 【Note Q Q28】
// P_input:### what_S1: [[Note P Q28]]
// P_output:### what_S1: 【Note P Q28】
//
// Tips: Modify it as needs.
// #####################################################################
// Added by Justdoitcc 2023-01-07 14:00
let b_disable_raw_hyperlink = false;
// M13. define HEADER_LEVEL_OF_FILENAME_TO_RENDER:
// the HEADER_LEVEL of the filename to render
// H1~H6: 1, 2, 3, 4, 5, 6
// Tips: Modify it as needs.
// #####################################################################
const HEADER_LEVEL_OF_FILENAME_TO_RENDER = 3;
// M15. define HEADER_LEVEL_OF_TAGS_OF_HEADING_TO_RENDER:
// the HEADER_LEVEL of the Tags_Of_Heading to render
// H1~H6: 1, 2, 3, 4, 5, 6
// Tips: Modify it as needs.
// #####################################################################
const HEADER_LEVEL_OF_TAGS_OF_HEADING_TO_RENDER = 6;
// M21. define REGEX_HEADING_IN_MULTI_LINE:
// Non-Capturing Group(?:x)
// non-greedy y*?
// u modifier: unicode. Pattern strings are treated as UTF-16. Also causes escape sequences to match unicode characters
// m modifier: multi line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)
// #####################################################################
// const REGEX_HEADING_IN_MULTI_LINE = /#{1,6}\s(?:.*?)/u;
const REGEX_HEADING_IN_MULTI_LINE = /^#{1,6}\s(?:.*?)$/um;
// M22 define REGEX_FENCED_CODE_BLOCKS_IN_MULTI_LINE:
// a markdown file may contain a Fenced Code Block
// u modifier: unicode. Pattern strings are treated as UTF-16. Also causes escape sequences to match unicode characters
// m modifier: multi line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)
// s modifier: single line. Dot matches newline characters
// g modifier: global. All matches (don't return after first match)
//
// Backreference: \1
// dv.span("This is a hello hello world!".match(/(hello) \1 \S+/g));
// =>hello hello world!
//
// A Test File:dic_19670901
// ### CASE_J #tag9
// ````ad-info
// ## input_data: J #tagJ
// ### JJJ #tagJ
// - J10
// - J20
// ````
//
const REGEX_FENCED_CODE_BLOCKS_IN_MULTI_LINE = /^(````*).+?\n(.+?)\1\n$/umsg;
// M23. define REGEX_HEADING_IN_SINGLE_LINE:
// Non-Capturing Group(?:x)
// non-greedy y*?
// Positive Lookbehind(?<=A) + Non-Capturing Group(?:x) + Positive Lookahead(?=Z)
//
// OS Abbreviation Escape sequence
// Windows : CR+LF \r\n
// UNIX : LF \n
// MAC : CR \r
//
// u modifier: unicode.
// m modifier: multi line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)
// g modifier: global. All matches (don't return after first match)
// #####################################################################
// Remarked by Justdoitcc 2023-01-05 16:00
// USING_SPLIT: To require M23 + M31.FR16+ M31.FR20
// const REGEX_HEADING_IN_SINGLE_LINE_USING_SPLIT = /^#{1,6}\s(?:.*?)$/u;
const REGEX_HEADING_IN_SINGLE_LINE = /^(#{1,6}\s.*?)(?=\r?\n|\r)$/umg;
// M25. define aoh_rows : to store key-value pairs of each raw heading
// #####################################################################
let aoh_rows = [];
// M27. update aoh_rows :
// transform a JavaScript array into a Dataview data array by using dv.array
// #####################################################################
aoh_rows = dv.array(aoh_rows);
// M31. gather each heading of a markdown page:
// #####################################################################
for (let page of pages) {
// M31.FR10 define s_entire_content_of_md_file: a muti-line string
// To read the entire content of a markdown page into a variable
// page.file.path=
// "100_Project/01_dataviewjs/01_by_example/Q28_heading/Q28_test_data/03/dic_19670301.md"
// #####################################################################
let s_entire_content_of_md_file = await app.vault.readRaw(page.file.path);
// M31.FR12 define b_md_contains_a_heading:
// #####################################################################
let b_md_contains_a_heading = REGEX_HEADING_IN_MULTI_LINE.test(
s_entire_content_of_md_file
);
// M31.FR14 check b_md_contains_a_heading:
// check if a markdown file contains a heading
// #####################################################################
if (b_md_contains_a_heading === false){
continue;
}
// M31.FR15 update s_entire_content_of_md_file:
// To remove REGEX_FENCED_CODE_BLOCKS_IN_MULTI_LINE
// #####################################################################
s_entire_content_of_md_file = s_entire_content_of_md_file.replace(
REGEX_FENCED_CODE_BLOCKS_IN_MULTI_LINE,
""
);
// M31.FR16 define a_entire_content_of_md_file: a JavaScript array
// #####################################################################
// Remarked by Justdoitcc 2023-01-05 16:00
// USING_SPLIT: To require M23 + M31.FR16+ M31.FR20
// let a_entire_content_of_md_file = s_entire_content_of_md_file.split(/\r?\n|\r/);
// M31.FR20 define a_entire_headers_of_md_file:
// #####################################################################
// Remarked by Justdoitcc 2023-01-05 16:00
// USING_SPLIT: To require M23 + M31.FR16+ M31.FR20
// let a_entire_headers_of_md_file = a_entire_content_of_md_file.filter((row) =>
// REGEX_HEADING_IN_SINGLE_LINE_USING_SPLIT.test(row)
// );
// str.match(/regex/g) returns all matches as an array, but capturing groups are not included.
let a_entire_headers_of_md_file = s_entire_content_of_md_file.match(
REGEX_HEADING_IN_SINGLE_LINE
);
// M31.FR30 define REGEX_HEADING_WITH_HASHTAG_S_NAME_S_TAGS:
// input:## milestones 10 #tag1
// output:
// Hashtag="##"
// Name="milestones 10 "
// Tags="#tag1"
// Positive Lookbehind(?<=A) + Non-Capturing Group(?:x) + Positive Lookahead(?=Z)
// Named Capture Group(?<Name>y)
// #####################################################################
const REGEX_HEADING_WITH_HASHTAG_S_NAME_S_TAGS =
/^(?<Hashtag>#+)\s(?<Name>[^#]+)?((?<=\s)(?<Tags>#[\p{Letter}0-9_/-]+)(?=\s*))?/u;
// M31.FR50 gather the key-value pairs from the s_single_line:
// #####################################################################
for (let s_single_line of a_entire_headers_of_md_file) {
// M31.FR50.FR11 define aMatch:
// #####################################################################
// str.match(/regex/g) returns all matches as an array, but capturing groups are not included.
let aMatch = s_single_line.match(
REGEX_HEADING_WITH_HASHTAG_S_NAME_S_TAGS
);
// M31.FR50.FR21 define i_Level_Of_Heading:
// #####################################################################
// dv.span(JSON.stringify(aMatch.groups.Hashtag.length, null, 2), "\n");
let i_Level_Of_Heading = aMatch.groups.Hashtag.length;
// M31.FR50.FR23 define s_Tags_Of_Heading:
// input:## what events 40
// output:
// Hashtag="##"
// Name="what events 40"
// Tags=undefined
// #####################################################################
let s_Tags_Of_Heading = "";
if (aMatch.groups?.Tags !== undefined){
s_Tags_Of_Heading = aMatch.groups.Tags;
}
// M31.FR50.FR25 define s_Heading_Without_Hashtag:
// #####################################################################
let s_Heading_Without_Hashtag = aMatch.groups?.Name + s_Tags_Of_Heading;
// M31.FR50.FR31 define s_single_line_with_bullet_point:
// #####################################################################
let s_single_line_with_bullet_point = "";
// M31.FR50.FR31A s_single_line_with_bullet_point:
// BULLET_CASE_11: Not to display the original level of each heading
// bullet point = bullet "•" U+2022
// #####################################################################
if (b_display_original_level === false) {
s_single_line_with_bullet_point =
" ".repeat(i_Level_Of_Heading) +
"• " +
s_Heading_Without_Hashtag;
}
// M31.FR50.FR31B s_single_line_with_bullet_point:
// BULLET_CASE_12: To display the original level of each heading
// bullet point = Lower Three Quarters Block "▆" U+2586
// #####################################################################
if (b_display_original_level === true) {
s_single_line_with_bullet_point =
"▆".repeat(i_Level_Of_Heading) +
" " +
s_Heading_Without_Hashtag;
}
// M31.FR50.FR41 define s_display_without_double_SQUARE_BRACKETS:
// if b_disable_raw_hyperlink
// Purposes:To disable the raw hyperlink of a raw heading as follows
// R_input:### what_S3: \[\[Note R Q28\]\]
// R_output:### what_S3: 【Note R Q28】
// Q_input:### what_S2: \[[Note Q Q28]\]
// Q_output:### what_S2: 【Note Q Q28】
// P_input:### what_S1: [[Note P Q28]]
// P_output:### what_S1: 【Note P Q28】
// #####################################################################
// Added by Justdoitcc 2023-01-07 14:00
if (b_disable_raw_hyperlink === true) {
let s_display_without_double_SQUARE_BRACKETS =
fun_disable_raw_hyperlink_of_raw_heading(
s_single_line_with_bullet_point
);
s_single_line_with_bullet_point =
s_display_without_double_SQUARE_BRACKETS;
}
// M31.FR50.FR51 define s_heading_link: \[[alphabet#a|a]\]
// #####################################################################
// let s_link = "[[" + page.file.path + "#" + Heading + "|" + Heading + "]]";
let s_heading_link = dv.sectionLink(
page.file.path,
s_single_line,
false,
// Remarked by Justdoitcc 2023-01-07 14:00
// s_display_without_double_SQUARE_BRACKETS
s_single_line_with_bullet_point
);
// M31.FR50.FR61 push the key-value pairs into the array aoh_rows:
// aoh_rows["values"] is a JavaScript array
// #####################################################################
aoh_rows["values"].push({
Raw_Heading: s_single_line,
Level_Of_Heading: i_Level_Of_Heading,
Heading_Without_Hashtag: s_Heading_Without_Hashtag,
Heading_Without_Tags: aMatch.groups?.Name,
Tags_Of_Heading: s_Tags_Of_Heading,
Name_Of_File: page.file.name,
Path_Of_File: page.file.path,
Link_Of_File: page.file.link,
Link_Of_Heading: s_heading_link
});
}
}
// M39. dedbug output aoh_rows:
// #####################################################################
// aoh_rows.forEach((h_row) => {
// if (
// h_row.Name_Of_File === "dic_19670401" &&
// h_row.Heading_Without_Tags === "milestones 10 "
// ) {
// dv.span("The following is the filtered content of the `aoh_rows` from the DVJS20.<br>");
// dv.span(JSON.stringify(h_row, null, 2), "\n");
// }
// });
//The following is the filtered content of the aoh_rows from the DVJS20.
// {
// Raw_Heading: "## milestones 10 #tag1",
// Level_Of_Heading: 2,
// Heading_Without_Hashtag: "milestones 10 #tag1",
// Heading_Without_Tags: "milestones 10 ",
// Tags_Of_Heading: "#tag1",
// Name_Of_File: "dic_19670401",
// Path_Of_File:
// "100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data/A04/dic_19670401.md",
// Link_Of_File: {
// path: "100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data/A04/dic_19670401.md",
// embed: false,
// type: "file",
// },
// Link_Of_Heading: {
// path: "100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data/A04/dic_19670401.md",
// embed: false,
// display: " • milestones 10 #tag1",
// subpath: "milestones 10 tag1",
// type: "header",
// },
// }
// M41. filter and sort aoh_rows:
// To filter by h_row.Tags_Of_Heading :
// To gather a heading where the Tags_Of_Heading is not undefined
// h_row.Tags_Of_Heading: "#tag1" , "#tag2", "#tag3" or undefined
// #####################################################################
aoh_rows = aoh_rows
.where((h_row) => h_row.Tags_Of_Heading);
// .sort((h_row) => h_row.Name_Of_File, "asc");
// M51. define groups:
// groupBy_CASE:To group each `h_row` of the `aoh_rows` by `h_row.Name_Of_File`
// (Results:)(key=G1.key)(values=G1.rows)
//
// Each `G1.key` is a value from the `h_row.Name_Of_File`.
// Each `G1.rows` is an Array of JavaScript Objects.
//
// h_row.Name_Of_File: "dic_19670401"
//
// (comments)sort_CASE:To sort each `G1.rows` of the `G1` by `G1.key` in descending order
//
// groupIn_CASE:To group each `G1.rows` of the `G1` by `G1.rows.Tags_Of_Heading`
// (Results:)(key=G2.key)(values=G2.rows=G1.rows.rows)
//
// Each `G2.key` is a value from the `G1.rows.Tags_Of_Heading`.
// Each `G2.rows` is an Array of JavaScript Objects.
//
// The Bottom Level(Grouped Twice): `G2`=`G1.rows`;` G2.rows`=`page`
//
// h_row.Tags_Of_Heading: "#tag1" , "#tag2", "#tag3" or undefined
// #####################################################################
let groups = aoh_rows
.groupBy((h_row) => h_row.Name_Of_File) // groupBy: (default) in ascending order
// .sort((G1) => G1.key, "desc")
.groupIn((G1) => G1.Tags_Of_Heading); // groupIn: (default) in ascending order
// M59. dedbug output groups:
// #####################################################################
// groups.forEach((group) => {
// if (
// group.key === "dic_19670401"
// ) {
// dv.span("The following is the content of the `group`.<br>");
// // dv.span(group.rows["values"][0].rows["values"][0]);
// // OK:dv.span(JSON.stringify(group.rows["values"][0].rows["values"][0], null, 2), "\n");
// dv.span(JSON.stringify(group.rows[0].rows[0], null, 2), "\n");
// }
// });
// The following is the content of the `group`.
// {
// Raw_Heading: "## milestones 10 #tag1",
// Level_Of_Heading: 2,
// Heading_Without_Hashtag: "milestones 10 #tag1",
// Heading_Without_Tags: "milestones 10 ",
// Tags_Of_Heading: "#tag1",
// Name_Of_File: "dic_19670401",
// Path_Of_File:
// "100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data/A04/dic_19670401.md",
// Link_Of_File: {
// path: "100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data/A04/dic_19670401.md",
// embed: false,
// type: "file",
// },
// Link_Of_Heading: {
// path: "100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data/A04/dic_19670401.md",
// embed: false,
// display: " • milestones 10 #tag1",
// subpath: "milestones 10 tag1",
// type: "header",
// },
// };
// M61. output groups:
// #####################################################################
for (let G1 of groups) {
// M61.FR01 define s_file_link :
// G1.key: "dic_19670301"
// #####################################################################
let s_file_link = dv.fileLink(
G1.rows[0].rows[0]["Path_Of_File"],
false,
G1.key
);
// M61.FR10 output s_file_link:
// G1.key:dic_19670301
// #####################################################################
dv.header(HEADER_LEVEL_OF_FILENAME_TO_RENDER, s_file_link);
// M61.FR12 sort G1.rows: in descending order
// G1.rows.key: "#tag1" , "#tag2", "#tag3", "#tag9"
// #####################################################################
// G1.rows = G1.rows.sort((gp) => gp.key, "desc");
// M61.FR20 output G1.rows:
// #####################################################################
for (let G2 of G1.rows) {
// M61.FR20.FR11 output Tags_Of_Heading:
// G2.key: "#tag1" , "#tag2", "#tag3"
// #####################################################################
dv.header(HEADER_LEVEL_OF_TAGS_OF_HEADING_TO_RENDER, G2.key);
// M61.FR20.FR13 output each Link_Of_Heading :
// #####################################################################
dv.span(G2.rows.Link_Of_Heading);
}
}
// T01. define fun_disable_raw_hyperlink_of_raw_heading(): return a string
// #####################################################################
// Purposes:To disable the raw hyperlink of a raw heading as follows
// R_input:### what_S3: \[\[Note R Q28\]\]
// R_output:### what_S3: 【Note R Q28】
// Q_input:### what_S2: \[[Note Q Q28]\]
// Q_output:### what_S2: 【Note Q Q28】
// P_input:### what_S1: [[Note P Q28]]
// P_output:### what_S1: 【Note P Q28】
function fun_disable_raw_hyperlink_of_raw_heading(text = "") {
// Remarked by Justdoitcc 2023-01-07 14:00
// return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
// return str.replace(/[[\]\\]/g, " ");
// F10. define sText:
// #####################################################################
let sText = text;
// F11. check sText :
// #####################################################################
if (sText === "") {
return sText;
}
// F13. define sTextCleaned:
// #####################################################################
let sTextCleaned = sText;
// F21. define IMPLICIT_FIELDS_OUTLINKS_REGEX: [[Note Q]]
// #####################################################################
// input:[[Note Q]] \[[Note J]\]
// output:\[\[Note Q\]\] \[\[Note J\]\]
// input:[[Note P]] \[[Note J]\]
// output:【Note P】 【Note J】
// Designed in dataview_v0.5.46
// An Obsidian link of the form [[<link>]].
// link: _ => parsimmon_umd_min.exports.regexp(/\[\[([^\[\]]*?)\]\]/u, 1)
// Non-Capturing Group (?:x) + non-greedy : (For better performance)
// const IMPLICIT_FIELDS_OUTLINKS_REGEX =
// /(?<=\s)(?:\[\[([^\[\]]*?)\]\])\s*,*/gu;
// F31. replace sTextCleaned:
// R_input:\[\[Note R Q28\]\]
// R_output:【Note R Q28】
// Q_input:\[[Note Q Q28]\]
// Q_output:【Note Q Q28】
// P_input:[[Note P Q28]]
// P_output:【Note P Q28】
// #####################################################################
// if (IMPLICIT_FIELDS_OUTLINKS_REGEX.test(sTextCleaned)) {
sTextCleaned = sTextCleaned
// R_input:\[\[Note R Q28\]\]
// R_output:【Note R Q28】
.replace(/\\\[\\\[/g, "【")
.replace(/\\\]\\\]/g, "】")
// Q_input:\[[Note Q Q28]\]
// Q_output:【Note Q Q28】
.replace(/\\\[\[/g, "【")
.replace(/\]\\\]/g, "】")
// P_input:[[Note P Q28]]
// P_output:【Note P Q28】
.replace(/\[\[/g, "【")
.replace(/\]\]/g, "】");
// }
// F90. return sTextCleaned:
// #####################################################################
return sTextCleaned;
}
```