Copying text as Markdown table – e.g. for TableFlip

I've been experimenting with http://tableflipapp.com/ and found that I needed something to copy tab-delimited text or Excel selections as Markdown tables. (Pipe-delimited, optionally with a ruler line).

You should be able to adapt an earlier KM macro by Chris Stone to do this (for TableFlip, it will need adjusting to flank the pipe characters with spaces).

Here, in the meanwhile, is a version which also adds a Markdown ruler after the first line – useful if you want to paste tables with headers into TableFlip.

This is a JS version – Sierra ES6 JS by default, but you can alternatively activate the pre-Sierra ES5 JS action which I have also put in there (back-compiled from ES6 by Appspot Closure).

USE

  • Select the Excel area or tab-delimited text
  • Run the macro to copy and rewrite the clipboard to MD
  • In TableFlip, choose File > New from Clipboard

( Or, in Marked 2, choose Clipboard > Preview )

Copy tab-delimited lines as Markdown table.kmmacros (22.9 KB)

ES6 JS Code (for ES5 pre Sierra version, see the alternative action in the macro)

(() => {
    'use strict';

    ObjC.import('AppKit');

    // textClip :: () -> Maybe String
    let textClip = () => {
            let strUTF8Type = "public.utf8-plain-text",
                pb = $.NSPasteboard.generalPasteboard;

            return ObjC.deepUnwrap(
                    pb.pasteboardItems.js[0].types
                )
                .indexOf(strUTF8Type) !== -1 ? ObjC.unwrap(
                    pb.stringForType(
                        strUTF8Type
                    )
                ) : undefined;
        },

        // lines :: String -> [String]
        lines = s => s.split(/[\r\n]/),

        // unlines :: [String] -> String
        unlines = xs => xs.join('\n'),

        // maximumBy :: (a -> a -> Ordering) -> [a] -> a
        maximumBy = (f, xs) => xs.reduce(function (a, x) {
            return a === undefined ? x : (
                f(x, a) > 0 ? x : a
            );
        }, undefined),

        // replicate :: Int -> a -> [a]
        replicate = (n, a) => {
            let v = [a],
                o = [];

            if (n < 1) return o;
            while (n > 1) {
                if (n & 1) o = o.concat(v);
                n >>= 1;
                v = v.concat(v);
            }
            return typeof a === 'string' ? (
                o.concat(v).join('')
            ) : o.concat(v);
        },

        mbClip = textClip();

    if (mbClip && mbClip.indexOf('\t') !== -1) {
        let a = Application.currentApplication(),
            sa = (a.includeStandardAdditions = true, a),

            lstRows = lines(mbClip)
            .map(x => x.split('\t')),

            lstMDLines = lstRows
            .map(lstRow => '| ' + lstRow.join(' | ') + ' |'),

            // Including a Markdown table ruler
            strClip = unlines(
                [lstMDLines[0]]
                .concat(replicate(
                    maximumBy(
                        (a, b) => a.length - b.length,
                        lstRows
                    ).length,
                    "|--"
                ) + "|").concat(lstMDLines.slice(1))
            );

        return (
            sa.setTheClipboardTo(
                strClip
            ),
            strClip
        );
    }

})();
1 Like

I have been using this KBM Macro with this java script for a while and has worked great. Still works but now it is adding an extra line between rows? Can anyone give me a clue as what needs to be changed in the script. This just started about a month ago.

Col1 Col2 Col3
Row1 100.00 200.00 300.00
Row2 200.00 300.00 400.00
Row3 300.00 400.00 500.00
Row4 400.00 500.00 600.00
Total 1,000.00 1,400.00 1,800.00

Thanks in advance

I think we would need to see the input text as well as the output table.

Could you paste an input output pair in this thread fenced above and below by triple backticks like this:

```

Lines of input text here

```

```

and lines of output MD here

```

What are you using to render/preview the table ?

Thank you for the prompt feedback, hope this helps

Input from Excel i.e. (copying a range a1..d6)

Test	             Col1	     Col2	    Col3
Row1	 100.00 	 200.00 	 300.00 
Row2	 200.00 	 300.00 	 400.00 
Row3	 300.00 	 400.00 	 500.00 
Row4	 400.00 	 500.00 	 600.00 
Total	 1,000.00 	 1,400.00 	 1,800.00 

Output after running Macro pasted into Markdown editor

| Test | Col1 | Col2 | Col3 | 
|--|--|--|--|
|  | 
| Row1 |  100.00  |  200.00  |  300.00  | 
|  | 
| Row2 |  200.00  |  300.00  |  400.00  | 
|  | 
| Row3 |  300.00  |  400.00  |  500.00  | 
|  | 
| Row4 |  400.00  |  500.00  |  600.00  | 
|  | 
| Total |  1,000.00  |  1,400.00  |  1,800.00  | 

I am using this to copy various excel information into my Obsidian notes. I am also getting the same output results posting into aiWriter and Typora which I tried just to see if the results were consistent. Tracked the issue back to Dec 18. Was working fine through Dec 17 then started adding the extra line space Dec 18 and onward. Thinking maybe a Mac OS, excel update or something must have triggered the change. Currently running MacOS 10.15.7 and Excel 16.45

(Glad this 2016 post re-emerged from the mists of time.) :slight_smile:

mdpre, which I authored and use daily, converts CSV to Markdown tables. It's a small part of what it does but here's a code snippet you can convert

=colwidth 1 2 4
=colalign r c l
=csv
"Column A","Column B","Column C"
17,Blue,This is a comment
29,Red,Another comment
=endcsv

I day "daily" because I build workshop PowerPoint slides using mdpre and md2pptx as part of my day job. Using these two a CSV file turns nicely into a PowerPoint table slide. You can fine tune such a slide using metadata either at the start of the Markdown file or on an individual slide basis.

One other point: You can adjust simple text run properties with a <style> element. mdpre allows me to include a metadata file and CSS (which is ignored by md2pptx) - so you can publish two ways:

  • Via md2pptx as a presentation.
  • Via a regular Markdown processor as a document / web page etc.

Hoping the above is a useful contribution to the "what shall we do with CSV if we want to turn them into Markdown?" discussion.

Before I take a closer look, the first thing that comes to mind in the context of Excel is Windows style line-endings, and I notice in the JS source code this definition of a lines function, which is splitting a clipboard string into a list of lines.

        lines = s => s.split(/[\r\n]/),

(with a trailling comma there, because its part of a sequence of definitions)


These days I would normally define a lines function as:

// lines :: String -> [String]
const lines = s =>
// A list of strings derived from a single
// string delimited by newline and or CR.
    0 < s.length ? (
        s.split(/[\r\n]+/u)
    ) : [];

(See, for example, https://github.com/RobTrew/prelude-jxa )

and a key difference between this and what we see in the older script is the splitting regex (where u specifies unicode and the + defines a line break as one or more instances of \r or \n.

It's possible that you may now be getting Windows-style \r\n line-breaks in the clipboard from Excel, whereas they were previously getting normalised to macOS/Unix-style plain \n.

If you have time to try a quick manual edit there from:

/[\r\n]/

to

/[\r\n]+/u

then we might get a clue, but in any case, I'll experiment this evening.

1 Like

That did the trick!

I edited the following line:

lines = s => s.split(/[\r\n]/),

And replaced with

lines = s => s.split(/[\r\n]+/u),

Thank you very much for your help and macro.

All the best

1 Like