JS :: Use Array.reduce to move done items to the end of the draft content


#1

Another use of the general swiss army knife Array.reduce – moving any completed items (prefixed with - [x] rather than - [ ]) to the end of the contents of a draft.

Again, Array.reduce generally lets us:

  • Start with some kind of seed value (the ‘accumulator’), and
  • work through a list, updating the accumulator value step by step.

In this case

  • The seed value is a JS dictionary object (like an Applescript record, but more flexible), which contains two lists, both initially empty, body:[] and end:[],
  • the step by step function has the usual two basic arguments – (accumulator, item) - in this case the accumulator is the body+end dictionary and the item is the current line.

It just appends the current line either to body:[] (current position), or to end:[] (bottom of draft).

Here is a reusable function:

  • from the text of an existing draft
  • to an updated version of that text (with completed items now at the end).
// completedItemsToEnd :: String -> String
const completedItemsToEnd = strDraft => {

    // dct :: { body :: [String], end :: [String] }
    const dct = strDraft.split('\n')
        .reduce(
            // Accumulator updated with every line.
            (a, line) => line.startsWith('- [x]') ? {
                body: a.body,
                end: a.end.concat(line)
            } : {
                body: a.body.concat(line),
                end: a.end
            },

            // Seed value of accumulator.
            {
                body: [],
                end: []
            }
        );
    return dct.body.concat(dct.end).join('\n');
};

#2

Or alternatively:

// completedItemsToEnd :: String -> String
const completedItemsToEnd = strDraft => {

    // dct :: { body :: [String], end :: [String] }
    const dct = strDraft.split('\n')
        .reduce(
            // Accumulator updated with every line.
            (a, line) => {
                const isDone = line.startsWith('- [x]');
                return {
                    body: isDone ? a.body : a.body.concat(line),
                    end: isDone ? a.end.concat(line) : a.end
                };
            },

            // Seed value of accumulator.
            {
                body: [],
                end: []
            }
        );
    return dct.body.concat(dct.end).join('\n');
};

#3

I’ve added this and executed it. I get the green success banner but the draft is unchanged - completed items do not move. Am I forgetting to turn something on?


#4

I get the green success banner but the draft is unchanged

Yes the function above is just a snippet which translates one string to another (within Javascript, without import from or exporting back to Drafts).

We can pull the raw string in from a draft, and then update the draft with the transformed string, by writing something like:

draft.content = (
    completedItemsToEnd(
        draft.content
    )
);
draft.update();

Or as the full code for an action:

// Completed items moved to bottom of draft
(() => {

    // main :: () -> IO Bool
    const main = () => {

        draft.content = (
            completedItemsToEnd(
                draft.content
            )
        );
        draft.update();

        console.log(true);
        return true;
    };

    // Helper function ------------------------------------

    // completedItemsToEnd :: String -> String
    const completedItemsToEnd = strDraft => {

        // dct :: { body :: [String], end :: [String] }
        const dct = strDraft.split('\n')
            .reduce(
                // Accumulator updated with every line.
                (a, line) => {
                    const isDone = line.startsWith('- [x]');
                    return {
                        body: isDone ? a.body : a.body.concat(line),
                        end: isDone ? a.end.concat(line) : a.end
                    };
                },

                // Seed value of accumulator.
                {
                    body: [],
                    end: []
                }
            );
        return dct.body.concat(dct.end).join('\n');
    };

    // MAIN -----------------------------------------------
    return main();
})()


JS :: More general use of Array.reduce - partitioning lines or drafts into two groups
#5

You’re a wizard!

That works perfectly. Thanks!