Skip to main content
Inspiring
June 22, 2022
Answered

Javascript to find a similar filename among all open documents?

  • June 22, 2022
  • 3 replies
  • 2203 views

Hi all,

This seems to be something I can't find an easy answer to.

I'm trying to take the filename of an active document, and use it to search through all open documents and find a close match.

For example:

Client-BDA_spec-sheet-US_v1.indd  <- if this is the active document

Client-TRA_spec-sheet-US_v2.indd

Client-BDC_spec-sheet-A4_v4.indd

Client-BDA_spec-sheet-A4_v1.indd  <- the script then finds this open document (perhaps making it active)

Client-TRA_spec-sheet-A4_v1.indd

Client-BDC_spec-sheet-US_v2.indd

 

Don't laugh but this is as far I got

var doc = app.activeDocument;
var docName = doc.name;

    function findMatch(){
	for(var i = 0; i < app.documents.length; i++){
		// I GIVE UP!!!
		}
}

 Any help is much appreciated, as always. J

This topic has been closed for replies.
Correct answer m1b

Ah I see now. It is the v1 or v2 that is a problem here. You are now saying that it's okay to match a v2 with a v1 document, so that's likely why my script didn't find it. Here's an updated version. I just added a new argument: ignoreThis, which is a regex that will be completely ignored (it'll simply be removed before the comparison) so now it'll match documents that have differing version numbers with this regex /_v\d+/ . I hope I've understood now!

- Mark

 

 

var doc = app.activeDocument,
    myA4Doc = getDocumentByNameFindChange(doc, /-US/, '-A4', /_v\d+/);

if (myA4Doc != undefined)
    app.activeDocument = myA4Doc;

/**
 * Make a document active, based on its
 * name, relative to another document.
 *
 * @9397041 {activeDoc} activeDoc - a Document
 * @9397041 {RegExp} findWhat - regex to find with
 * @9397041 {String} changeTo - text to change to
 * @9397041 {RegExp} ignoreThis - regex to ignore
 * @Returns {Document} the matched Document
 */
function getDocumentByNameFindChange(activeDoc, findWhat, changeTo, ignoreThis) {
    // store a version of the active doc's name
    // but with the replacement text
    var findThisName = activeDoc.name
        .replace(findWhat, changeTo)
        // and remove the ignored part
        .replace(ignoreThis, '');

    for (var i = 0; i < app.documents.length; i++) {
        var doc = app.documents[i];

        // don't want activeDoc
        if (doc === activeDoc)
            continue;

        // check if name matches (minus the ignored part)
        if (doc.name.replace(ignoreThis, '') == findThisName)
            return doc;
    }

    // if flow gets to here
    // no document was found
    // so return nothing
}

 

 

 


And here's another version that will toggle back and forth between A4 and US versions. You configure it with the two arrays you pass as arguments. findWhat and changeTo can be arrays in this version.

- Mark

var doc = app.activeDocument,
    myA4Doc = getDocumentByNameFindChange(doc, [/-US/, /-A4/], ['-A4', '-US'], /_v\d+/);

if (myA4Doc != undefined)
    app.activeDocument = myA4Doc;

/**
 * Make a document active, based on its
 * name, relative to another document.
 * 
 * findWhat and changeTo can be Arrays
 * such that if findWhat[i] matches active
 * document name then switch to document
 * named after doing changeTo[i].
 * @9397041 {activeDoc} activeDoc - a Document
 * @9397041 {Array[RegExp]} findWhat - regex to find with
 * @9397041 {Array[String]} changeTo - text to change to
 * @9397041 {RegExp} ignoreThis - regex to ignore
 * @Returns {Document} the matched Document
 */
function getDocumentByNameFindChange(activeDoc, findWhat, changeTo, ignoreThis) {

    var activeName = activeDoc.name,
        index = 0;

    // work out which findWhat is in document
    if (findWhat.hasOwnProperty('0')) {
        for (index = 0; index < findWhat.length; index++)
            if (findWhat[index].test(activeName))
                break;
    }

    else {
        // just put them in an array so we
        // can handle them the same later
        findWhat = [findWhat];
        changeTo = [changeTo]
    }

    // store a version of the active doc's name
    // but with the replacement text
    var findThisName = activeDoc.name
        .replace(findWhat[index], changeTo[index])
        // and remove the ignored part
        .replace(ignoreThis, '');

    for (var i = 0; i < app.documents.length; i++) {
        var doc = app.documents[i];

        // don't want activeDoc
        if (doc === activeDoc)
            continue;

        // check if name matches (minus the ignored part)
        if (doc.name.replace(ignoreThis, '') == findThisName)
            return doc;
    }

    // if flow gets to here
    // no document was found
    // so return nothing
}

3 replies

Marc Autret
Brainiac
June 23, 2022

Hi @JustyR 

 

The Levenshtein distance could provide a more 'agnostic' approach to your question. You just have to filter out the suffix

 

/_spec-sheet-[a-z0-9]+_v\d+\.indd$/i

 

before processing, as this part should be ignored by the algorithm (if I understand properly your last comment.)

 

Here is a possible implementation of the whole thing:

https://github.com/indiscripts/IdGoodies/blob/master/full/DocShowClosestByName.jsx

 

Not sure it answers your original question but it was fun to code 😉

 

Best,

Marc

JustyRAuthor
Inspiring
June 23, 2022

Hi, Marc, thanks for taking the time to write something so complete.

As you mentioned, it doesn't quite fit my needs though. It does switch docs when it finds a match but it's a bit too forgiving.

For example, if I run the script with the following four files open:

IR XRL-Spec Sheet-A4_v1.indd   <- ACTIVE document

IR XRL-Spec Sheet-US_v2.indd   <- target doc it is supposed to switch to

IR XRT-Spec Sheet-A4_v1.indd   <- but it switches to this one instead  

IR XRT-Spec Sheet-US_v1.indd 

 

It's as though the const MY_DROP_REGEX is being ignored.

J

m1b
Community Expert
June 23, 2022

Ah I see now. It is the v1 or v2 that is a problem here. You are now saying that it's okay to match a v2 with a v1 document, so that's likely why my script didn't find it. Here's an updated version. I just added a new argument: ignoreThis, which is a regex that will be completely ignored (it'll simply be removed before the comparison) so now it'll match documents that have differing version numbers with this regex /_v\d+/ . I hope I've understood now!

- Mark

 

 

var doc = app.activeDocument,
    myA4Doc = getDocumentByNameFindChange(doc, /-US/, '-A4', /_v\d+/);

if (myA4Doc != undefined)
    app.activeDocument = myA4Doc;

/**
 * Make a document active, based on its
 * name, relative to another document.
 *
 * @9397041 {activeDoc} activeDoc - a Document
 * @9397041 {RegExp} findWhat - regex to find with
 * @9397041 {String} changeTo - text to change to
 * @9397041 {RegExp} ignoreThis - regex to ignore
 * @Returns {Document} the matched Document
 */
function getDocumentByNameFindChange(activeDoc, findWhat, changeTo, ignoreThis) {
    // store a version of the active doc's name
    // but with the replacement text
    var findThisName = activeDoc.name
        .replace(findWhat, changeTo)
        // and remove the ignored part
        .replace(ignoreThis, '');

    for (var i = 0; i < app.documents.length; i++) {
        var doc = app.documents[i];

        // don't want activeDoc
        if (doc === activeDoc)
            continue;

        // check if name matches (minus the ignored part)
        if (doc.name.replace(ignoreThis, '') == findThisName)
            return doc;
    }

    // if flow gets to here
    // no document was found
    // so return nothing
}

 

 

 

JustyRAuthor
Inspiring
June 22, 2022

Had a brainwave and got it working, albeit not quite in the same way as described in my initial post.

@m1b gave me a solution to a very similar problem, which I've used to get this one working.

#target InDesign

var doc = app.activeDocument;
var docName = doc.name;
var docName = getDocumentByRegex(/-A4/g);

if (docName != undefined)
    app.activeDocument = docName;

function getDocumentByRegex(regex) {
    for (var i = 0; i < app.documents.length; i++) {
        var doc = app.documents[i];
        if (regex.test(doc.name))
            return doc;
    }
}

 

 

 

m1b
Community Expert
June 23, 2022

Hi @JustyR, I'm a little worried that the function I supplied for the other question might fail in this case. It will find the *first* document with -A4, which might not always match the client code of your current document. How about this function? Can you see what it does? Let me know if you have questions.

- Mark

 

 

var doc = app.activeDocument;
var myA4Doc = getDocumentByNameFindChange(doc, /-US/, '-A4');

if (myA4Doc != undefined)
    app.activeDocument = myA4Doc;

/**
 * Make a document active, based on its
 * name, relative to another document.
 * @param {activeDoc} activeDoc - a Document
 * @param {RegExp} findWhat - regex to find with
 * @param {String} changeTo - text to change to
 */
function getDocumentByNameFindChange(activeDoc, findWhat, changeTo) {
    // store a version of the active doc's name
    // but with the replacement text
    var findThisName = activeDoc.name.replace(findWhat, changeTo);

    for (var i = 0; i < app.documents.length; i++) {
        var doc = app.documents[i];

        // don't want activeDoc
        if (doc === activeDoc)
            continue;

        // check if name matches
        if (doc.name == findThisName)
            return doc;
    }

    // if flow gets to here
    // no document was found
    // so return nothing
}

 

 

 

JustyRAuthor
Inspiring
June 23, 2022

Thanks Mark. I tried it but it didn't do anything (didn't switch to a matched doc). I wonder if it needs a more robust regex or even a split, like Bob suggested. 

As shown in my first post, the first 3/4 of the filename changes but will always end with either "-US-v#.indd" or "-A4-v#.indd". So it's trying to find a match for the first part that counts, something like this regex (/(.*)(?=-US|A4)/i)

J

Inspiring
June 22, 2022

Assuming that you want to match the documents for Client-BDA, you might try this after you define docName:

var clientName = docName.split("_")[0];

which will be the first piece of the name of the active document. In your loop, then, you also split the names of the other open documents to find a match. 
Not sure what you want to do if there are multiple matches, but item [1] of docName.split will be a string starting with "spec-sheet" and item [2] will be the version number. So you could use a second and third loop to check for matches in the second and third pieces of the name. 

Hope this helps,

 Bob