Dataviewjs - Group by Individual List Values

What I’m trying to do

So, essentially, I have all these book notes with a subject property. The subject property is a list, like:

---
subject:
- "fantasy & magic"
- "action & adventure"
- "inspirational"
- "science fiction"
---

And I want to have a graph that gives me percentages for each of these “subjects” individually. For example, I’m sure at least 30% of the books I’ve read have cats as a value for subject.

But instead I get veeery narrow pie slices because the lists are treated as full strings rather than individual values.

Do you know this can be achieved?

I have a faint idea in my head of iterating through each book’s subjects and counting each one as an instance of the book, and then using that to calculate the value, if you understand what I mean. However, I am not very skilled in Javascript, so I have no idea as to how I would do that.

Things I have tried

This is my current code in the picture:

let pages = dv.pages().where(p => p.status == "read" || p.status == "#read"); // only get books I have read already
let graphText = ""; // declare variable for use in the pie chart graph later

// I sort of copied this from someone, but the big idea:  produces the subject values and corresponding total counts, then formats it for the pie chart.
for (let group of pages.groupBy(b => b.subject)) {
   graphText += `"${group.key}" : ${Math.round(group.rows.file.name.length)}
   `;
}

// mermaid pie chart graph output
dv.paragraph(`
~~~mermaid
pie title Tags
 ${graphText}
 ~~~
 `) 

If you decide to help me out, thanks; I really appreciate it, because I love pie graphs and my Javascript is pathetic.

You’re grouping on the subject lists in your script. You need to flatten the subject lists before you group on similar subjects.

That makes sense, thank you!

Although, now I get this:

From this:

let pages = dv.pages().where(p => p.status == "read" || p.status == "#read").where(p => p.subject); // only get books I have read already, where subject isn't empty
let graphText = ""; // declare variable for use in the pie chart graph later

//  produce the subject values and corresponding total counts, then formats it for the pie chart.

for (let group of pages.groupBy(b => Array(b.subject).flat())) { // ADDED .flat function
   graphText += `"${group.key}" : ${Math.round(group.rows.file.name.length)}
   
   `;
}

// mermaid pie chart graph output
dv.paragraph(`
~~~mermaid
pie

 ${graphText}
 ~~~
 `)

I added the .flat() function to the grouping. Not sure if that is the correct way to do it. I’ve read that FLATTEN does not exist in dataviewjs the same as it does in the regular query language, and from top web results it seems that .flat() should do it, but I must not be using the right strategy.

I’d rather loop on the original set of notes, and make a temporary table to collect the number of the various subjects. Your code should then look something like the following:

```dataviewjs

let subjects = {} // Will hold the count of each subject

let pages = dv.pages()
  // only get books I have read already
  .where(p => p.status == "read" || p.status == "#read")
  // Loop and count each of the subjects
  .subject.forEach(s => {
    if ( s in subjects ) {
      subjects[s] = subjects[s] + 1
    } else {
      subjects[s] = 1
    }
  })

let graphText = ""; // declare variable for use in the pie chart graph later

// Now subjects holds each subject with the count of notes it appearead in,
// so lets loop on that and produce the data for the pie chart
for (const [subject, count] of Object.entries(subjects)) {
   graphText += `  "${subject}" : ${count}\n`;
}

// mermaid pie chart graph output
dv.paragraph(`
~~~mermaid
pie title Subjects
${graphText}
~~~
`)
```

Which in my test setup produced this pie chart:
image

Whoo, it works! Thank you so, so, so much! You’re like a JavaScript ninja!

The loop thing is very convenient; thanks for sharing. I will have to run it through ChatGPT to find exactly how it works, though. :smile:

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.