List all headings in a file

Hey all!

I have a bunch of files with a lot of headers. I would like, for each of those files, to list each headers, and if possible, sort them by the tags they have assigned, using Dataview.

To give a concrete example, I have a file with

# Header 1 #tag1
Some text.

# Header 2 #tag2
Some more text.

# Header 3 #tag1
Some more text.

I would like to obtain a list:

- Header 1 #tag1
- Header 2 #tag2
- Header 3 #tag1

and if possible, also sorted by tag:

# tag1
- Header 1 
- Header 3

# tag2
- Header 2

Thanks in advance!

Check out the Dynamic Table of Contents plugin

(This plugin might not be supported any more; but it still works well.)

Heders/sections aren’t an implicit data.

Regular dataview can’t do that… only if you target tasks or lists inside sections (because sections are associated data in tasks/lists).

I guess that will be possible in dataviewjs if you load the full content via dv.io.load() or obsidian api and apply some regex to a manual parsing…

Thanks a lot. Do you have any tips on how to use the dv.io.load() and regex? I know which regex I would use, but not really how to parse them with dataview…

Topic : 1/2

Summary
  • How to list entire headings in different files? (DVJS10)
  • How to groupBy filename and groupIn tags of each heading in different files? (DVJS20)

Test

Summary
  • dataview: v0.5.46

Input

Summary

dictionary files:

  • Location: “100_Project/01_dataviewjs/01_by_example/Q28_eHeadings/Q28_test_data”

folder: P03

  • filename : dic_19670301
---
Date: 1967-03-01
---
#Project/P03

# Header 1 #tag1
Some text.

# Header 2 #tag2
Some more text.

# Header 3 #tag1
Some more text.



folder: A04

  • filename : dic_19670401
---
Date: 1967-04-01
---
#Project/P04

## milestones 10 #tag1
1967-04-01 add this feature_A #Test/d01 [[Note J]] , [[Note K]]
1967-04-02 add this feature_B #Test/d02 [[Note J]] 

### milestones 10.01 #tag1
Some text 10.01

## some events 20 #tag2
1967-04-01 | drinks | Black Coffee | 11 | #Test/d01 [[Note J]] , [[Note K]]
1967-04-02 | drinks | Black Coffee | 21 | #Test/d02  [[Note J]]

### some events 20.01 #tag2
Some text 20.01

## other events 30 #tag3
info:: 1967-04-01 | drinks | Black Coffee | 12 | #Test/d01 [[Note J]] , [[Note K]]
info:: 1967-04-02 | drinks | Black Coffee | 22 | #Test/d02  [[Note J]]



folder: Q07_no_Tags_Of_Heading

  • filename : dic_19670701
---
Date: 1967-07-01
---
#Project/P07


## what events
The raw heading does not contain Tags_Of_Heading.
 


folder: Q08_no_Name

  • filename : dic_19670801
---
Date: 1967-08-01
---
#Project/P08


## 
The raw heading does not contain Name.



folder: Q09_excluding_Fenced_Code_Blocks

  • filename : dic_19670901
---
Date: 1967-09-01
---
#Project/P09

> The contents within the following 'Fenced Code Blocks' should be skipped when each heading is to be gathered.

### CASE_J #tag9
````ad-info 
## input_data: J #tagJ
### JJJ  #tagJ
- J10
- J20
````

### CASE_K #tag9
```yaml
### input_data: K #tagK
#### KKKK #tagK
- K01
- K02
```


### CASE_P #tag9
`````ad-example
## input_data: P #tagP1


````ad-info
## input_data: P #tagP2

```ad-success
## input_data: P #tagP3

```

````


```ad-bug
## input_data: P #tagP4

```


`````


### CASE_Q #tag9
````ad-info
## input_data: Q #tagQ1

```ad-success
## input_data: Q #tagQ2

```

````


### CASE_Skipped  #tag9
    ````ad-warning
    ## input_data: P5
    ````



folder: Q10_disable_hyperlink

  • filename : dic_19671001
---
Date: 1967-10-11
---
#Project/P10


## what: How to disable the raw hyperlink of a raw heading?

### what_S1: [[Note P Q28]] #tag10
- To replace "[[Note P Q28]]" with "【Note P Q28】"

### what_S2: \[[Note Q Q28]\] #tag10  
- To replace "\[[Note Q Q28#bbb]\]" with "【Note Q Q28#bbb】"

### what_S3: \[\[Note R Q28\]\] #tag10
- It is OK.
- To replace "\[\[Note R Q28#ccc\]\]" with "【Note R Q28#ccc】"

### what_S4:\[\[ #tag10
- It is OK. 
- To replace "\[\[" with "【"

### what_S5:\]\] #tag10
- It is OK.
- To replace "\]\]" with "】"



folder: Q11_Never_Escape_Regex_Characters

  • filename : dic_19671101
---
Date: 1967-11-01
---
#Project/P11


## what: Never to remove the 16 Escape regex characters of a raw heading

### what_T1:/.*+?^${}()|[]\ 
- The 15 special characters are not to be removed in the DVJS10 or DVJS20 except those in the file like 'dic_19671001'.
- They are to be removed in any `file.lists`.

### what_T2:-
- The special character like "-" is not to be removed in the DVJS10 or DVJS20.
- It is not to be removed in any `file.lists`.



folder: Y12_no_headings

  • filename : dic_19671201
---
Date: 1967-12-01
---
#Project/P12



DVJS10_List_entire_headings_groupBy_filename_via_readRaw_of_Obsidian_API

Summary

Main DVJS

Code Name Data type Group By Purposes Remark
DVJS10
_List_entire_headings
_groupBy_filename
_via_readRaw_of_Obsidian_API
(A customizable variable:
Modify it as needs.)
b_display_original_level:
1.false
2.true

(A customizable variable:
Modify it as needs.)
b_disable_raw_hyperlink:
1.false
2.true

(A customizable variable:
Modify it as needs.)
HEADER_LEVEL_OF_FILENAME_TO_RENDER:
1~6

(A variable)
aoh_rows:
an array of JavaScript Objects
yes
(once)
1.To define a aoh_rows (to store key-value pairs of each raw heading)

2.To filter and sort the aoh_rows(M41):
2.1 To gather a heading which matches the regular pattern
2.2 To gather a heading like H1, H2, H3

3.To group each h_row of the aoh_rows by h_row.Name_Of_File
(Results:)(key=G1.key)(values=G1.rows)
{The Bottom Level(Grouped Once): G1.rows=page}

4.To display the result:
4.1 To display each G1.key(Name_Of_File) as a heading element(H4)
4.2 To display each G1.rows.Link_Of_Heading as a list
4.3 The G1.rows.Link_Of_Heading is transformed into a link via the dv.sectionLink function.

Notes

Summary

Q1: How to filter by page.file.name? (M11)

Summary_Q1
Original Example: Q1 (To be modified)
```dataviewjs
// 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"));


```

A1_11:

Another Example: A1_11
```dataviewjs
// 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"));

```

Q2: How not to filter by h_row.Heading_Without_Hashtag and h_row.Level_Of_Heading? (M41)

Summary_Q2
Original Example: Q2 (To be modified)
```dataviewjs
// M41. filter and sort aoh_rows:
// (comments)WHERE_CASE_10:To gather each heading which contains "milestones" 
// WHERE_CASE_11:To gather a heading which matches the regular pattern 
//               like /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/
// WHERE_CASE_12:To gather a heading like H1, H2, H3
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    // .where((h_row) =>
    //     dv.func.contains(h_row.Heading_Without_Hashtag, "milestones")
    // )
    .where((h_row) =>
        /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/.test(
            h_row.Heading_Without_Hashtag
        )
    )
    .where((h_row) => h_row.Level_Of_Heading <= 3);
    // .sort((h_row) => h_row.Name_Of_File, "asc");

```

A2_21:

Another Example: A2_21
```dataviewjs
// M41. filter and sort aoh_rows:
// (comments)WHERE_CASE_10:To gather each heading which contains "milestones" 
// WHERE_CASE_11:To gather a heading which matches the regular pattern 
//               like /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/
// WHERE_CASE_12:To gather a heading like H1, H2, H3
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
// aoh_rows = aoh_rows
    // .where((h_row) =>
    //     dv.func.contains(h_row.Heading_Without_Hashtag, "milestones")
    // )
    // .where((h_row) =>
    //     /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/.test(
    //         h_row.Heading_Without_Hashtag
    //     )
    // )
    // .where((h_row) => h_row.Level_Of_Heading <= 3)
    // .sort((h_row) => h_row.Name_Of_File, "asc");


```

Q3: What does the following code mean? (M41:WHERE_CASE_11)

Summary_Q3
Original Example: Q3 (To be explained)

Methods:
WHERE_CASE_11: To use one Regular Expression in the where statement instead of the ‘||’( or ) operators

Purposes:
1.To simplify the ‘||’( or ) operators in the where statement
2.To deal with a large number of different conditions

```dataviewjs
// M41. filter and sort aoh_rows:
// (comments)WHERE_CASE_10:To gather each heading which contains "milestones" 
// WHERE_CASE_11:To gather a heading which matches the regular pattern 
//               like /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/
// WHERE_CASE_12:To gather a heading like H1, H2, H3
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    // .where((h_row) =>
    //     dv.func.contains(h_row.Heading_Without_Hashtag, "milestones")
    // )    
    .where((h_row) =>
        /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/.test(
            h_row.Heading_Without_Hashtag
        )
    )
    .where((h_row) => h_row.Level_Of_Heading <= 3);
    // .sort((h_row) => h_row.Name_Of_File, "asc");

```

A3_31:

Another Example: A3_31

WHERE_CASE_11: To use ten ‘||’( or ) operators in the where statement

```dataviewjs
// M41. filter and sort aoh_rows:
// (comments)WHERE_CASE_10:To gather each heading which contains "milestones" 
// WHERE_CASE_11:To gather a heading which matches the regular pattern 
//               like /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/
// WHERE_CASE_12:To gather a heading like H1, H2, H3
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    // .where((h_row) =>
    //     dv.func.contains(h_row.Heading_Without_Hashtag, "milestones")
    // )    
    .where(
        (h_row) =>
            dv.func.contains(h_row.Heading_Without_Hashtag, "Header") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "milestones") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "some") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "other") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "what") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "CASE_J") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "CASE_K") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "CASE_P") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "CASE_Q") ||
            dv.func.contains(h_row.Heading_Without_Hashtag, "CASE_S") ||
            h_row.Heading_Without_Hashtag === undefined
    ) 
    .where((h_row) => h_row.Level_Of_Heading <= 3);
    // .sort((h_row) => h_row.Name_Of_File, "asc");

```

Code DVJS10_List_entire_headings_groupBy_filename_via_readRaw_of_Obsidian_API

Summary_code
title: DVJS10_List_entire_headings_groupBy_filename_via_readRaw_of_Obsidian_API =>1.To define a `aoh_rows` (to store key-value pairs of each raw heading) {The Bottom Level(Grouped Once): `G1.rows`=`page`} 2.To filter and sort the `aoh_rows`(M41): 2.1 To gather a heading which matches the regular pattern 2.2 To gather a heading like H1, H2, H3 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 display the result: 4.1 To display each `G1.key`(`Name_Of_File`) as a heading element(H4) 4.2 To display each `G1.rows.Link_Of_Heading` as a list 4.3 The `G1.rows.Link_Of_Heading` 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 = 4;


// 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:
    // input:## milestones 10 #tag1
    // output:
    // Hashtag="##"
    // Name="milestones 10 #tag1"
    // 
    // Positive Lookbehind(?<=A) + Non-Capturing Group(?:x) + Positive Lookahead(?=Z)
    // Named Capture Group(?<Name>y)
    // #####################################################################    
    const REGEX_HEADING_WITH_HASHTAG_S_NAME = /^(?<Hashtag>#+)\s(?<Name>.+)?$/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);
        

        // 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
        // #####################################################################
        // The following is used in th DVJS20.
        // 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:
        // #####################################################################
        // The following is used in th DVJS20.
        // let s_Heading_Without_Hashtag = aMatch.groups?.Name + s_Tags_Of_Heading;
        let s_Heading_Without_Hashtag = aMatch.groups?.Name;
        

        // 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,
            
            // The following is used in th DVJS20.
            // 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_Hashtag === "milestones 10 #tag1"
//     ) {
//         dv.span("The following is the content of the `aoh_rows` from the DVJS10.<br>");
//         dv.span(JSON.stringify(h_row, null, 2), "\n");
//     }
// });

//The following is the content of the aoh_rows from the DVJS10.
// {
//     Raw_Heading: "## milestones 10 #tag1",
//     Level_Of_Heading: 2,
//     Heading_Without_Hashtag: "milestones 10 #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:
// (comments)WHERE_CASE_10:To gather each heading which contains "milestones" 
// WHERE_CASE_11:To gather a heading which matches the regular pattern 
//               like /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/
// WHERE_CASE_12:To gather a heading like H1, H2, H3
// Tips: See the output of the M39 and modify the M41 as needs.
// #####################################################################
aoh_rows = aoh_rows
    // .where((h_row) =>
    //     dv.func.contains(h_row.Heading_Without_Hashtag, "milestones")
    // )    
    .where((h_row) =>
        /Header|milestones|some|other|what|CASE_[JKPQS]|undefined/.test(
            h_row.Heading_Without_Hashtag
        )
    )
    .where((h_row) => h_row.Level_Of_Heading <= 3);
    // .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.
// 
//              The Bottom Level(Grouped Once): `G1.rows`=`page`
//
// Name_Of_File: "dic_19670401"
//
// (comments)sort_CASE:To sort each `G1.rows` of the `G1` by `G1.key` in descending order
// #####################################################################
let groups = aoh_rows
    .groupBy((h_row) => h_row.Name_Of_File) // groupBy: (default) in ascending order
    //.sort((G1) => G1.key, "desc");


// M61. output groups:
// #####################################################################
for (let G1 of groups) {
        
    // M61.FR01 define s_file_link :
    // #####################################################################   
    let s_file_link = dv.fileLink(
        G1.rows.Path_Of_File[0],
        false,
        G1.rows.Name_Of_File[0]
    );


    // M61.FR10 output page.file.link :
    // #####################################################################
    // dv.header(HEADER_LEVEL_OF_FILENAME_TO_RENDER, G1.key + ":");
    dv.header(HEADER_LEVEL_OF_FILENAME_TO_RENDER, s_file_link);
    
    
    // M61.FR20 output each Link_Of_Heading :
    // #####################################################################    
    dv.span(G1.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;
}



```

Screenshots(DVJS10): PartA

  • let b_display_original_level = false; // (default)
  • let b_disable_raw_hyperlink = true;
PartA 1/2 :

PartA 2/2 :

Screenshots(DVJS10): PartB

  • let b_display_original_level = true;
  • let b_disable_raw_hyperlink = true;
PartB 1/2 :

PartB 2/2 :


1 Like

Topic : 2/2

Summary
  • How to list entire headings in different files? (DVJS10)
  • How to groupBy filename and groupIn tags of each heading in different files? (DVJS20)

DVJS20_List_entire_headings_groupBy_filename_groupIn_Tags_Of_Heading_via_readRaw_of_Obsidian_API

Summary

Main DVJS

Code Name Data type Group By Purposes Remark
DVJS20
_List_entire_headings
_groupBy_filename
_groupIn_Tags_Of_Heading
_via_readRaw_of_Obsidian_API
(A customizable variable:
Modify it as needs.)
b_display_original_level:
1.false
2.true

(A customizable variable:
Modify it as needs.)
b_disable_raw_hyperlink:
1.false
2.true

(A customizable variable:
Modify it as needs.)
HEADER_LEVEL_OF_FILENAME_TO_RENDER:
1~6

(A customizable variable:
Modify it as needs.)
HEADER_LEVEL_OF_TAGS_OF_HEADING_TO_RENDER:
1~6

(A variable)
aoh_rows:
an array of JavaScript Objects
yes
(twice)
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.

Notes

Summary

Q1: How to sort the groups by Name_Of_File in descending order? (M51)

Summary_Q1
Original Example: Q1 (To be modified)
```dataviewjs
// 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

```

A1_11:

Another Example: A1_11
```dataviewjs
// 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

```

Q2: How to sort the groups by Tags_Of_Heading in descending order? (M61.FR12)

Summary_Q2
Original Example: Q2 (To be modified)
```dataviewjs
// M61. output groups:
// #####################################################################
for (let G1 of groups) {

    
    
    // M61.FR12 sort G1.rows: in descending order
    // G1.rows.key: "#tag1" , "#tag2", "#tag3", "#tag9"
    // #####################################################################
    // G1.rows = G1.rows.sort((gp) => gp.key, "desc");

```

A2_21:

Another Example: A2_21
```dataviewjs
// M61. output groups:
// #####################################################################
for (let G1 of groups) {
    
    
    
    // M61.FR12 sort G1.rows: in descending order
    // G1.rows.key: "#tag1" , "#tag2", "#tag3", "#tag9"
    // #####################################################################
    G1.rows = G1.rows.sort((gp) => gp.key, "desc");

```

Code DVJS20_List_entire_headings_groupBy_filename_groupIn_Tags_Of_Heading_via_readRaw_of_Obsidian_API

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;
}



```

Screenshots(DVJS20): PartA

  • let b_display_original_level = false; // (default)
  • let b_disable_raw_hyperlink = true;
PartA 1/2 :

PartA 2/2 :

Screenshots(DVJS20): PartB

  • let b_display_original_level = true;
  • let b_disable_raw_hyperlink = true;
PartB 1/2 :

PartB 2/2 :


Reference

Summary

Emojis

The groupIn function

The Markdown syntax

The Regular Expression

Obsidian_API