Script snippets (1) 'reduce' -> outline width and depth


#1

How many layers of indentation does this TaskPaper outline have ?
And how many leaves (lines without further ‘children’ indented below them) ?

We could find out by writing something like this:

(function () {
    'use strict';

    // TASKPAPER CONTEXT
function TaskPaperContext(editor, options) {

    // The sum of the leafCounts of all the item's children
    // (or just 1, if it has no children)
    // leafCount :: tp3Item -> Int
    function leafCount(item) {
        return item.hasChildren ? item.children
            .reduce(function (value, child) {
                return value + leafCount(child);
            }, 0) : 1;
    }

    // One more than the layerCount of the deepest of all its children
    // layerCount :: tp3Item -> Int
    function layerCount(item) {
        return item.hasChildren ? item.children.map(layerCount)
            .reduce(function (value, n) {
                return value < n ? n : value;
            }, 0) + 1 : 0;
    }

    var root = editor.outline.root;

    return {
        leaves: leafCount(root),
        layers: layerCount(root)
    }
}

// JAVASCRIPT FOR AUTOMATION
var ds = Application("TaskPaper")
    .documents;
    return ds.length ? ds[0].evaluate({
        script: TaskPaperContext.toString(),
    }) : undefined;
})()

TaskPaper provides the item.hasChilden test here, and the item.children list, while JavaScript provides Array.reduce(function, initialValue) and Array.map(function) – two very generally useful alternatives to looping through a list of values.

Simple examples of .reduce (sometimes called fold or foldl in other languages) might be:

(function () {
    'use strict';


    // sum :: [a] -> Num
    function sum(xs) {
        var startValue = 0;

        return xs
            .reduce(function (value, x) {
                return value + x;
            }, startValue);
    }

    // product :: [a] -> Num
    function product(xs) {
        var startValue = 1;

        return xs
            .reduce(function (value, x) {
                return value * x;
            }, startValue);
    }


    var lst = [1, 2, 3, 4, 5, 6, 7, 8, 9];

    return {
        sum: sum(lst),
        product: product(lst)
    }
})();

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
and:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

If you are more accustomed to AppleScript, you can see examples of how AppleScript can be coaxed into doing something similar at:

http://rosettacode.org/wiki/Catamorphism#AppleScript