Sorting files by name in DataviewJS

Thanks to @holroy’s help I have a script that finds PDFs with names that contain the name of the current file (great for finding receipts for an ‘organization’ page). I even expanded it to work with aliases of the current file (well, the first 3 aliases at least). It also lists Incoming links, and both sections only appear if there is content, otherwise they are hidden:

const filename = dv.current().file.name
const aliases = dv.current().file.aliases
const docpath = '_Resources/Documents'
const files = app.vault.getFiles().filter(file => file.path.startsWith(docpath) && (file.name.includes(filename) || file.name.includes(aliases[0]) || file.name.includes(aliases[1]) || file.name.includes(aliases[2])))
const linksIn = dv.current().file.inlinks
let headers = "No"
if(Array.isArray(files) && files.length){
    headers = "Yes"
    dv.header(2,"Documents")
    dv.span("******")
    dv.list(files.map(file => dv.fileLink(file.path)))
}
if(Array.isArray(linksIn) && linksIn.length){
    headers = "Yes"
    dv.header(2,"Incoming Links")
    dv.span("******")
    dv.list(linksIn)
}
if(headers === "No"){
    dv.span(".")
}

Every file I want to show this has the following code that calls the script:

```dataviewjs
dv.view("Meta/Scripts/PlacesFooter")
```

Works great with one exception: The file names are not sorted. It’s generally close to sorted, but not always. For example, on one page, here’s how the file names start:

  • 20230318
  • 20230217
  • 20221019
  • 20221220
  • 20221226
  • 20220824

You can see the third row is out of place in a descending sort.
I’ve tried:

const files = app.vault.getFiles().filter(file => file.path.startsWith(docpath) && (file.name.includes(filename) || file.name.includes(aliases[0]) || file.name.includes(aliases[1]) || file.name.includes(aliases[2]))).sort(file => file.name, "desc")

Doesn’t have any effect. I’ve looked through a lot of posts, and one mentioned the fact that even though my posts begin with numbers, they will be sorted alphabetically. No idea how I’d force it to use numbers instead of characters (plus in my example, I don’t think it’s sorting alphabetically). Other posts talk about sorting by ctime, but in my case ctime isn’t accurate as I’ve added old files recently, so the date at the beginning of the file name (YYYYMMDD) is what should govern the order. I’m at a loss, anyone have any suggestions?

I missed out on commenting the sorting option on the other post, my bad.

Sorting within dataviewjs is a little icky, as it requires you to define a sort function which returns -1 if the first item comes before the second item, 0 if they’re the same, and 1 if the first item comes after the second item.

If this was only numbers you could do:

.sort( (a, b) => a - b )
// or if the number was in a.number
.sort( (a, b) => a.number - b.number )

When dealing with strings within objects, this needs some logic to produce the correct output, or the use of a dedicated function. This function which should work nicely in your case is the String.prototype.localeCompare(). Using this in combination with how sort() expects its parameters we end up with something like:

```dataviewjs
const files = app.vault.getFiles()
  .filter(file => file.path.startsWith(docpath) && 
          (file.name.includes(filename)   ||
           file.name.includes(aliases[0]) || 
           file.name.includes(aliases[1]) || 
           file.name.includes(aliases[2])  )
  )
  .sort( (a, b) => a.name.localeCompare(b.name) )
```

Hopefully that should then sort your file names as per your preferences.

You definitely know your stuff! I’m still struggling to understand the language, but using your code, and adding a ‘.reverse()’ at the end has got me where I wanted to be. It’s sorting perfectly now. Thanks again, you rock!

Alternatively you should be able to add a minus sign in front of the localeCompare function instead of doing the reverse. Untested, but it should work, I think, possibly, I reckon… :smiley:

Yup, that works! For anyone playing along at home, the final code ends up being:

const filename = dv.current().file.name
const aliases = dv.current().file.aliases
const docpath = '_Resources/Documents'
const files = app.vault.getFiles()
    .filter(file => file.path.startsWith(docpath) && 
        (file.name.includes(filename) ||
        file.name.includes(aliases[0]) || 
        file.name.includes(aliases[1]) || 
        file.name.includes(aliases[2]))
    )
    .sort( (a, b) => -a.name.localeCompare(b.name))
const linksIn = dv.current().file.inlinks
let headers = "No"
if(Array.isArray(files) && files.length){
    headers = "Yes"
    dv.header(2,"Documents")
    dv.span("******")
    dv.list(files.map(file => dv.fileLink(file.path)))
}
if(Array.isArray(linksIn) && linksIn.length){
    headers = "Yes"
    dv.header(2,"Incoming Links")
    dv.span("******")
    dv.list(linksIn)
}
if(headers === "No"){
    dv.span(".")
}

Thanks again @holroy !

1 Like

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