Export photo captions to CSV test Macro (v11.0.2)

I have a list of photo captions in a two-column CSV in this format:

"Filename","Caption"

I need to split the CSV into three columns like this

"Filename","Species","Caption"

The regular expression seems to work ok. So I think my usual failure to understand what's happening inside the For Each action is where it's going wrong. Any advice please?

Export photo captions to CSV test.kmmacros (7.9 KB)

Here are a couple of actions that I wrote to achieve what you want. I did it slightly differently from you because I was intimidated by that long regex. I put the sample material in the For Each text box, but you can change that to a variable easily enough.

Actions (v11.0.3)

Keyboard Maestro Actions.kmactions (3.5 KB)

Keyboard Maestro Export

I took a look at it. No, it doesn't look okay. I turned on the debugger and found out that the regex you are using is assigning the entire line into the third variable. I usually use the debugger to troubleshoot regex, but most people refer using the website "regex101.com". Or if you want a simpler regex that works, try using my macro instead.

One alternative to regular expressions:

  • splitting each line into parts
  • appending or prepending double-quote characters where needed
  • rejoining the split parts

ALTERNATIVE approach Export photo captions to CSV.kmmacros (6.2 KB)


Expand disclosure triangle to view JS source
const main = () =>
    lines(kmvar.PhotoCSV)
        .flatMap(
            line => 0 < line.length
                ? (() => {
                    const parts = line.split(",");

                    return [
                        // Filename.
                        parts[0],

                        // Species with end quote.
                        `${parts[1]}"`,

                        // Caption with start quote.
                        [
                            `"${parts[2].trim()}`,
                            ...parts.slice(3)
                        ]
                            .join(",")
                    ]
                        .join(",")
                })()
                : []
        )
        .join("\n");

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

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

// MAIN ---
return main();

The third action -- you're putting each line to process into the variable PhotoCaption but you're searching the entirety of PhotoCSV.

Remember, the commonest use for a "For Each" is to grab pieces of a larger thing to process each piece in turn:

image

Screenshot 2025-02-18 at 9.29.23 am

Not sure whether the For Each lines collection depends on finding \n as the line delimiter,
but if it does, I think you may find that the CSV lines in the OP's source are \r delimited.

"Lines in" is EOL-agnostic -- a good reason to use it!

EOL Demo.kmactions (1.8 KB)

Note lack of a blank "Display Text" for the Windows EOL CRLF combo. Sweet!

1 Like

:+1:

and I guess another way to use it here (to place the missing " characters),
might be with custom (,) delimiters, and writing to indexed parts, in Keyboard Maestro variable arrays.

So, for example (using Variable Array assignments, with either "," or ", " as custom delimiter):

VARIABLE ARRAY approach to Export photo captions to CSV.kmmacros (6.6 KB)

If we can rely on the text format for that, then I think we can just do everything with a single global "Search and Replace":

Export photo captions to CSV (S'n'R).kmmacros (4.9 KB)

Searching for:

(?m)^"(.*?)","([^,]*),\s(.*)$

...and replacing with:

"\1","\2","\3

Thanks all, this was simply a case of changing the variable being searched inside the For Each action to PhotoCaption.