Script: Sorting top level projects alphabetically


@complexpoint, I tried running this code without selecting Archive and searches, and for some reason, it keeps placing the “Archive” project all the way up there. My approach was to change,

outline.evaluateItemPath( '/@type=project' )

to something like,

outline.evaluateItemPath( '/* except (archive union @search)' )

  1. Do you have any idea of how to change that behavior?

  2. I was also wondering if it would be possible to change the behavior of the sort to sort by the second element of the string. I have first and last names combined as a project name (so the first and last name are separated by a simple space), but ideally, I would like to sort by last name. I would think that this would be something a lot more complicated, but just wondering if you have something like this already done somewhere in your quiver.


without selecting Archive and searches

Not quite sure what you mean there – this was just a basic demonstration of scripting an alphabetic sort of top level projects.

Selection state is not referenced in the code – presumably ‘Archive’ is high in the A-Z sequence of your projects ?

the second element of the string

You may have to expand a bit there …

The name of the project has two elements ?


Sorry about the obtuseness. If I run the script, the saved searches in the document and the Archive project are moved to the top. This is confusing since by default they are added at the end of the document. So that, I wanted to sort top level projects without including the “Archive” project the search queries.

I figured that since you had this on the code,

outline.evaluateItemPath( '/@type=project' )

I could just select the top level projects except the search functions and the Archive project. That was the reason of the code I included,

outline.evaluateItemPath( '/* except (archive union @search)' )

Now. Onto my second question. I have some projects organized like this,

John Lennon:
Tim Apple:
Victor Gutierrez:

But I wanted to be able to organize it alphabetically by last names. Something like,

Tim Apple
Victor Gutierrez
John Lennon


I think it could be fairly tricky to script sorting of only the selected subset of paragraphs …

Feels intuitively like the kind of thing that could easily go wrong and drop a para or two somewhere between the cracks – I’m afraid my personal appetite for scripting is more or less limited to rather safer and less ambitious operations : - )


Re family-name sorting, it’s possible that something like this might work:

(() => {
    'use strict';

    const sortTopLevelProjects = (editor, options) => {

        const rgxSpace = /\s+/;

        // secondWord :: TP Item -> String
        const secondWord = x => {
            const ks = x.bodyString.split(rgxSpace);
            return (
                1 < ks.length ? (
                ) : ks[0]

        // projName :: TP Item -> String
        const projName = x => x.bodyString.toLowerCase();

        // fKey :: TP Item ->String
        const fKey = options.sortKey.startsWith('second') ? (
        ) : projName;

        const main = () => {
            const outline = editor.outline;

            outline.groupUndoAndChanges(() => {

                        (nextSibling, oProj) => {
                            // binds it to the name 'nextSibling'
                            // for next item
                            return oProj;
                        // initialize nextSibling for the rightmost item
            return JSON.stringify(options);

        // GENERIC -------------------------------------------------

        // comparing :: (a -> b) -> (a -> a -> Ordering)
        const comparing = f =>
            (x, y) => {
                    a = f(x),
                    b = f(y);
                return a < b ? -1 : (a > b ? 1 : 0);

        // main :: IO ()
        return main();

    const ds = Application('TaskPaper').documents;
    return ds.length ? ds[0].evaluate({
        script: sortTopLevelProjects.toString(),
        withOptions: {
            sortKey: 'second'
    }) : 'no document found in TaskPaper ...';


Thank you so much. This is super useful. I was trying to play with the code a little bit to understand it better. I am not sure what this part of the code does.


Since I have tried to modified that to focus on different parts of the project to no avail. Am I misreading something into what is the job of that evaluate?

This is important to me, because I would still like to not include the Archive project into the sort or if possible, even change that evaluation to include some more complex cases. Is this something possible?
Again, Thank you very much.


That search path defines the set of objects that are sorted, so you could technically exclude any project named archive by editing that section of the code to:

    '/(@type=project) and (not archive)'


In this implementation, excluding that project from the AZ order would, I think, invariably place it at the top of the file, before anything starting with aa or ab, ac etc, and I’m not sure if that’s really what you want.


Thank you, that explains why no matter what I tried there, everything I didn’t want to sort ended up at the top of the file.