How to use DataviewJS to provide filtered lists of tags in vault, not pages with tags?

What I’m trying to do

Using DataviewJS to return a list of the nested tags that exist in my vault, but filtered to only a few level 1 roots.

Example:

In the vault, there are the following tags
#Food
#Food/Burger
#Food/Burger/Cheese
#Food/Burger/Cheese/American
#Food/Burger/Cheese/Cheddar
#Food/Burger/Cheese/Swiss
#Food/Royal
#Clothes
#Clothes/Kilts
#Clothes/Pants
#Clothes/Shirt
#Clothes/Socks
#archive
#_system

I need a list output that would show (with or without the # sign) as:
#Food
#Food/Burger
#Food/Burger/Cheese
#Food/Burger/Cheese/American
#Food/Burger/Cheese/Cheddar
#Food/Burger/Cheese/Swiss
#Food/Royal
#Clothes
#Clothes/Kilts
#Clothes/Pants
#Clothes/Shirt
#Clothes/Socks

It would also be extremely helpful to list sub-levels, such as starting with #Food/Burger/Cheese to get a list like this:
#Food/Burger/Cheese/American
#Food/Burger/Cheese/Cheddar
#Food/Burger/Cheese/Swiss

Things I have tried

I’ve been trying to figure out how to list tags for a week, but I’m not a javascript programmer and all of my google-fu yields the many ways you can use tags to get lists of pages. None of the documentation for Dataview has led me to believe that it can even see a list of tags, but I’m hoping this is where javascript changes what is possible…

1 Like

This is what I use:

```dataviewjs
const fields = {}
Object.values(app.metadataCache.metadataCache)
  .forEach(x => {
    (x.tags || [])
      .forEach(y => fields[y.tag] = -~fields[y.tag])
  })

dv.header(3, 'Tags')
dv.list(Object.keys(fields)
  .sort((a, b) => a.localeCompare(b))
  .map(x => `${x} (${fields[x]} notes)`))
```

(although I sort by number of notes descending. For anyone who’d prefer that way, change the sort line to be .sort((a, b) => fields[b] - fields[a]) instead.)

To filter for your specific tags, before the .sort line add:

  .filter(tag => tag.startsWith('#Food') || tag.startsWith('#Clothes'))
1 Like

Thank you for the snippets!
I’m not getting all of my tags this way, unfortunately. To continue the example, when I use the example as-is and when I try to focus on them with the filter line as you provided, I get the #Food based tags but the #Clothes tags are simply not in either output type.

I’ve confirmed they exist in the vault and are assigned to pages, but appear to be missing from the app.metadataCache.metadataCache object values. :frowning:

I’ve used:

dataviewjs
const fields = {}
Object.values(app.metadataCache.metadataCache)
  .forEach(x => {
    (x.tags || [])
      .forEach(y => fields[y.tag] = -~fields[y.tag])
  })

dv.header(3, 'Tags')
dv.list(Object.keys(fields))

and

dataviewjs
const fields = {}
Object.values(app.metadataCache.metadataCache)
  .forEach(x => {
    (x.tags || [])
      .forEach(y => fields[y.tag] = -~fields[y.tag])
  })

dv.header(3, 'Tags')

dv.list(Object.keys(fields)
   .filter(tag => tag.startsWith('#Food') || tag.startsWith('#Clothes'))
)

Perhaps app.metadataCache.getTags() will give you what you want? I never noticed that function before so it might be newish.

```dataviewjs
const tags = app.metadataCache.getTags()

dv.header(3, 'Tags')
dv.list(Object.keys(tags)
  .map(tag => `${tag} (${tags[tag]} notes)`))
```

By the way, if you open the developer console in Obsidian by pressing Ctrl+Shift+i, you can type app then press enter, and you’ll be able to browse through the functions available in Obsidian via Javascript.

Thank you for the additional insight! This has all helped immensely for all kinds of reasons.

In fairness, though, your last statement is steeped in the curse of expertise. The gap I face between:

By the way, just open the developer console in Obsidian by pressing Ctrl+Shift+i, and you can type app then press enter…

…and…

… you’ll be able to find out pretty much anything that you can do in Obsidian via Javascript.

…is a very vast ocean of fundamental current Javascript knowledge and experience which allows you to navigate that output and know what is or isn’t applicable or viable that I don’t have and am very unlikely to bridge with any statement that starts with “just”.

I simply don’t have the internal context to use this insight effectively at this time. :frowning:

I appreciate the sentiment, but I would need way smaller bite sized pieces to make use of that statement.

No, that’s not fair. I was offering it as a suggestion based off your comment that you were looking at “the app.metadataCache.metadataCache object values”, which led me to believe you had some knowledge of looking at object values.

That’s why I said “By the way” as an aside after already providing a full answer to the original question. I was explaining my own process of discovering the getTags() function (by browsing the app object), in case that method of discovery was of use to you.

What a crass thing to say to someone who was trying to help you.

2 Likes

I honestly did not mean offense to you; regardless of my intent, I do apologize. I am quite in your debt and thank you again for the help.

I do ask that you consider that your own words say exactly the same, emphasis mine:

I was explaining my own process of discovering the getTags() function (by browsing the app object), in case that method of discovery was of use to you.

Your own process… which is driven by experience and allows you to understand when and why you would put that in anything or exactly what to do with the information when you read it… due to expertise.

I say all of this with no malice what-so-ever; not only is it a simple trap that we all fall into as we get better at anything over time, but also I only meant to dispel any idea that I was anything more than sly monkey banging on keys hoping to write a sonnet or maybe a haiku when it comes to this topic.

I’m also sorry that my self-deprecation does not translate well. :frowning:

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