Sort at current depth (sort currently selected node and its siblings)

I think it may be possible to generalize this very helpful code in two stages:

To work with any tag:
(ISO 8601 date strings are sortable, so fetching a string value should be enough, I think)

Edit the value of strTagName in this script:

// Sort currently selected node and its siblings by due date.
(() => {
    'use strict';

    const tp3JSContext = (editor, options) => {
        'use strict';
        const
            selection = editor.selection.startItem, // Get the selected node (or the starting item of a selected range)...
            selection_parent = selection.parent; // ... and the parent of the selected node.

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

                const
                    tagValue = strTag => item =>
                    item.hasAttribute('data-' + strTag) ? (
                        item.getAttribute('data-' + strTag)
                    ) : '';

                 // CHANGE TAG NAME HERE (e.g. 'done')
                const strTagName = 'due';

                const sorted_siblings =
                    selection_parent.children
                    .sort(comparing(tagValue(strTagName)))

                sorted_siblings.forEach( // Uproot the unsorted nodes from their common parent...
                    x => x.removeFromParent()
                );

                selection_parent.appendChildren(sorted_siblings); //... and graft them back in the right order.
            }
        );
    }

    const
        tp3 = Application('TaskPaper'),
        ds = tp3.documents;
    return ds.length ? ds[0].evaluate({
        script: tp3JSContext.toString()
    }) : undefined;
})();

To allow for secondary and tertiary sorts
(each value-getting function paired with either true for AZ, or false for ZA)

(edit the value of tagName at the bottom of the script to sort, for example, by ‘done’ rather than ‘due’)

// Sort currently selected node and its siblings by due date.
(options => {
    'use strict';

    const tp3JSContext = (editor, options) => {
        'use strict';

        // compare :: a -> a -> Ordering
        const compare = (a, b) => a < b ? -1 : (a > b ? 1 : 0);

        // mappendComparing :: [((a -> b), Bool)] -> (a -> a -> Ordering)
        const mappendComparing = fboolPairs =>
            (x, y) => fboolPairs.reduce(
                (ord, fb) => {
                    const f = fb[0];
                    return ord !== 0 ? (
                        ord
                    ) : fb[1] ? (
                        compare(f(x), f(y))
                    ) : compare(f(y), f(x));
                }, 0
            );

        const
            tagValue = strTag => item =>
            item.hasAttribute('data-' + strTag) ? (
                item.getAttribute('data-' + strTag)
            ) : '';

        const
            // The parent of the selected node.
            selection_parent = editor.selection.startItem.parent,
            sorted_siblings =
            selection_parent.children
            .sort(
                mappendComparing(
                    [ // Sorted by rising date,
                        [tagValue(options.tagName), true],
                        // then by text AZ
                        [x => x.bodyContentString, true]
                    ]
                )
            );

        return (
            editor.outline.groupUndoAndChanges(() => {
                // Uproot the unsorted nodes from their common parent...
                sorted_siblings.forEach(
                    x => x.removeFromParent()
                );

                //... and graft them back in the right order.
                selection_parent.appendChildren(sorted_siblings);
            }),
            sorted_siblings.map(x => x.bodyString)
            .join('\n')
        );
    };

    // JXA CONTEXT -----------------------------------------------------------

    const
        tp3 = Application('TaskPaper'),
        ds = tp3.documents;
    return ds.length ? ds[0].evaluate({
        script: tp3JSContext.toString(),
        withOptions: options
    }) : undefined;
})({
    tagName: 'due'
});
4 Likes