Dataview Plugin

I use Obsidian to organise a lot of Info about DnD, both general and about my campaign. I am trying to create a table through dataview that sorts all spells from a specific class in descending order. It works out well for every spell that only has the one class I’ve been doing, which is druid.

TABLE level, casting, range, components, duration, school
FROM #spells
WHERE class = "druid"
SORT file.name asc

This is the table.


class: “druid”
tag: spells, druid
level: 5
casting: 1 action
range: Self
components: V, S
duration: Concentration, up to 1 hour
school: Abjuration

This is the info for one of the spells where it works.

I want to have a spell table for every class so I need to find out how to give one spell several classes. If there is a spell, that has several classes, it doesn’t put it into the table.


class: “druid”, “ranger”, “sorcerer”, “wizard”
tag: spells, druid, ranger, sorcerer, wizard
level: 1
casting: Reaction
range: Self
components: S
duration: 1 round
school: Abjuration

This spell needs 4 different class ‘tags’ (not quite tags but you know what I mean), but as soon as I add something after druid, it deletes it out of the druid spell table.
I don’t know if I managed to bring my question across but I appreciate the help!

try this for your where line:
WHERE contains(class,"druid")

https://blacksmithgu.github.io/obsidian-dataview/reference/functions/#contains-and-friends

1 Like

First order of business is to make your properties valid, and that class property is not a valid property (as you can see if you switch to reading view and/or look at the file properties by themself).

Here is a test document showcasing some alternatives of lists in various contexts:

---
AnArray: ["druid", "ranger", "sorcerer", "wizard"]
AList:
- druid
- ranger
- sorcerer
- wizard
---

The next line is accepted by _dataview_, but not as a property
InlineList::  "druid", "ranger", "sorcerer", "wizard"

## Display lists and length of list
Not a list: `= this.InlineList `  (`= length(this.InlineList)`)
An array: `= this.AnArray ` (`= length(this.AnArray)`)
A list: `= this.AList ` (`= length(this.AList)`)

If you try to move the InlineList to the properties/frontmatter (and remove the extra colon), you’ll get invalid properties. But you could declare your class as either of the array notation or the list notation. If you manipulate your list using the property editor, they’ll be converted to the list notation afterwards!

When you got the list, then @cheezopath has the correct idea of using contains() to check the list for values. However, you might run into an issue if any of your classes are substrings of other classes, and you’ve got some classes defined as single entries, like in class: "druid".

Imagine having one file with class: "drunken druid", and another with a list like class: "druid", "ranger", "wizard". If you do contains(class, "druid") both those files would match this statement. I set up these classes in my test environment, and ran these queries:

## Contains grievance
### Pure contains()
```dataview
LIST class
FROM "ForumStuff/f71/f71236"
WHERE contains(class, "druid")
```

### Pure econtains()
```dataview
LIST class
FROM "ForumStuff/f71/f71236"
WHERE econtains(class, "druid")
```

### List conversion and econtains()
```dataview
LIST class
FROM "ForumStuff/f71/f71236"
WHERE econtains(flat(list(class)), "druid")
```

Which yielded this result:

Notice how the two first variants included the Drunk file with the “drunken druid” class, but the last query doesn’t do that. This is a constructed example, but you hopefully get the point.

The trickery of flat(list(class)) is that it first makes a list() no matter what out of the class property. If it’s a list from before, it’ll become a list with two levels, and if it just was a text it becomes a simple list. To get rid of that multi-level list in the first case, we apply flat() to that list which effectively moves all elements into one giant list.

The various list conversion constructs

Here is a query and the result when doing various list manipulation on the class property in my test scenario to showcase the previous paragraph.

```dataview
TABLE class, typeof(class), list(class), flat(list(class)), typeof(flat(list(class)))
FROM "ForumStuff/f71/f71236"
WHERE file != this.file
```

The goal was to get a consistent result independent on whether class held a single text, or is a list of text. In the second and third column we can see these cases, and how they differ in presentation and types. In the fourth column we force them into being lists, and you can see the multilevel list for the “Multiple” file.

Finally in the fifth and sixth column we can see the flattened version and the corresponding type. And that is the one which is preferable to use for consisting handling of single text and list of text properties. Now using contains() and friends are reliable again. (Note that contains() does a partial match against any list item, and econtains() does an equality match on the entire list item)

2 Likes

this is such a detailed reply, thanks so much!

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