• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Create an array of arrays from a .txt file!

LEGEND ,
Nov 03, 2016 Nov 03, 2016

Copy link to clipboard

Copied

Hi all scripters,

The beginning is a .txt file containing data as:

text_1;text_12

text_2;text_23

text_3;text_39

I'm able to get an array as:

[text_1;text_12, text_2;text_23, text_3;text_39, …]

But I need that:

[ ["text_1","text_12"], ["text_2","text_23"], ["text_3","text_39"], …]

I've absolutely no idea about the way to get this array of arrays!!

Thanks for your ideas!

(^/)

TOPICS
Scripting

Views

3.4K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 04, 2016 Nov 04, 2016

Copy link to clipboard

Copied

Hi Obi-wan,

one split() and one loop with another split() can get you there.

Depending on the line separator—\n or \r—that could be:

var array1 = string.split("\r");

var array2 = [];

for(var n=0;n<array1.length;n++)

{

    array2[array2.length++] = array1.split(";")

};

Regards,
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 04, 2016 Nov 04, 2016

Copy link to clipboard

Copied

Hi Uwe,

It seemed interesting but I can't make it work in my script.

The deal: I have 4 Layers [Layer_1, Layer_2, Layer_3 and Layer_4] and I want to change their name but not necessarily for all!

So I have a list (.txt file):

Layer_1;Layer_12

Layer_2;Layer_23

To validate the code, I've temporarily pasted this list directly into the .jsx:

myList = ["Layer_1;Layer_12","Layer_2;Layer_23"];

var myLayers = app.activeDocument.layers;

for (var L = 0; L < myLayers.length; L++) {

        var myLayerName_0 = myLayers.name;

        var mLength_0 = myLayerName_0.length;

        for (var N = 0; N < myList.length; N++) {

                var mList_0 = myList;

                myConcordance = mList_0.slice(0,mLength_0);

                myLayerName_1 = mList_0.slice(mLength_0+1,mList_0.length);

                   

                    if ( myLayerName_0 == myConcordance ) var myLayerName_0 = myLayerName_1;

        }

}

The writing seems logical but nothing happens in the Layers panel! 

Tired! Thanks in advance! 

(^/)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Hi Obi-wan,

before talking about your code, I want to talk about the separator string—";"—you are using with your list:


It should be one, that is not used in the listed names of layers. And that could become quite difficult since it seems like every possible character is allowed for forming a name of a layer. Even a tabulator character could be used in a layer name: "\t”. It could not be typed in, but it could be copy/pasted to the Name input field.


So the question boils down to:
What is a good separator string for a concordance list, old name|separator|new name for renaming layers?

How about:
"\u0016"

That is used as a special character for tables with InDesign.

I tried to copy/paste it to the Name input field of the Layers panel, but it was stripped out while pasting.

Still one could assign a name using "\u0016" as part of a string to a layer's name by scripting:

app.documents[0].layers[0].name = "N"+"\u0016"+"N";

Screenshot after running this line:

SpecialCharacter-used-with-LayerName.png

So it's not 100% save to use a delimiter like that.
Does someone has a better idea?

Hm.

Would a Windows user be able to insert "\u0016" to the Name edit field using the keyboard by pressing Alt+0016 ?

Or Alt 022 or Alt 22 ?
Cannot test this, currently I'm on Mac OSX.

Regards,
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Hi Uwe,

A little misunderstanding between us! 

I've ";" in my .txt file because it's an export from Excel in .txt with as horizontal separator = ";".

The user wants to change layer names! So he creates a concordances table in Excel.

First column = actual layer name

2nd column = new layer name.

The next step of my writing will be to extract this list directly from the .txt file [I've already written the code].

Just have to insert it when I'll be able to manipulate its contents! Not yet apparently! 

(^/)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

I don't think, I misunderstood.
I spoke a bit in general terms about compiling a concordances table.


If your customer isn't using the horizontal separator ";" in a layer name, all is starting ok.

But sometimes a list is compiled by reading out a lot of documents by scripting where you cannot check individually what's in a layer's name. And then my notes on what would be the best delimiter would become relevant very soon.

Best regards,
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Back to your essential coding problems.

Here an example that could work for you:

/**

* @@@BUILDINFO@@@ RenamingLayers-Using-ConcordanceList.jsx !Version! Sat Nov 05 2016 15:05:59 GMT+0100

*/

// Uwe Laubender

// Make sure, that the separatorString cannot be used in a regular layer name

// "oldName;newName" would perhaps not work, because ; could be part of oldName ( or newName ).

// I've seen a lot when it comes to naming layers with customer documents.

// This could be a candidate:

var notUsedCharacterInNames = "\u0016";

// But for the sake of Obi-wan's example we use the following one:

notUsedCharacterInNames = ";";

// Scheme: oldName+separatorString+newName

var listArray =

    [

        "Layer_1"+notUsedCharacterInNames+"Layer_12",

        "Layer_2"+notUsedCharacterInNames+"Layer_12" // I did this by purpose

    ];

var myDoc = app.documents[0];

// Execute function:

renameLayersFromList(myDoc , listArray , notUsedCharacterInNames);

// Arguments of function:

// 1. doc  [object Document]

// 2. listArray  [object Array] of concordance strings in the form:

// oldString+separatorString+newString

// 3. notUsedCharacterInNames  [object String],

// the separator string from item 2, that has dual use in the function below:

// A. To split() the entry in the provided list to new and old

// B. To check in the layer names array of the document if the new name is already in use

function renameLayersFromList(doc , listArray , notUsedCharacterInNames)

{

    // Loop through the listArray:

    for(var n=0;n<listArray.length;n++)

    {

        // Implemented for preventing errors with the naming process.

        // Note: Names of layers must be unique.

        var error = false;

      

        // Split the incoming item of the listArray:

        var splitArray = listArray.split(notUsedCharacterInNames);

      

        // No error checking here on the result, but maybe should be done.

        // The provided list could be malformed.

        var oldName = splitArray[0];

        var newName = splitArray[1];

        // Before renaming we should check two things:

        // 1. Does the old name listed correspond with a valid layer name?

        if(!doc.layers.itemByName(oldName).isValid){error = true};

        // 2. Is the new name in conflict with an already existing layer name?

        if(app.documents[0].layers.everyItem().name.join(notUsedCharacterInNames).match(newName)){error = true};

        // If an error ocsurred, we loop on.

        // You could also track the error and write a detailed report.

        if(error){continue};

        // No errors? Fine. Do the renaming:

        doc.layers.itemByName(oldName).name = newName;

      

    }

} // function renameLayersFromList()

Have a nice weekend, Obi-wan!
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Uwe,

Nice learning for me WE! 

See you soon! … and have a nice WE too!

(^/)

[ I've tested your code! Cool! Now, just need to study, understand and be able to replicate it in other situations! Thanks a lot! ]

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Hi,

You forget somewhere in your script to modifiy the layer name.

Here another attempt with 2 arrays : old names = oNames and new names = nNames

(function () {

    if (app.locale.toString() == "FRENCH_LOCALE") {

        var infos = {

            findFile:"Choisir le fichier contenant la liste de noms."

        };

    } else {

        var infos = {

            nodoc:"Select the text file for layer names."

        };

    }

    var txtList = "layer_names.txt", oNames = [], nNames = [];

    var myListFile = File(myFindFile(txtList));

    // datas

    var testNames = listNames();

    if (testNames && oNames.length == nNames.length) {

    //    alert(oNames + "\n" + nNames);

        var myLayers = app.activeDocument.layers;

        for (var L = 0; L < myLayers.length; L++) {

            var testN = false;

            for (var n = 0; n < oNames.length && !testN; n++) {

                if (myLayers.name == oNames) {

                    myLayers.name = nNames;

                    // ensure old name doesn't conflict with same new name :

                    oNames = "";

                    testN = true;

                }

            } // for n

        } // for L

    }

    function listNames() {

        // Open the file for reading

        myListFile.open("r");

        var text = myListFile.read();

        var lines = text.split("\n");

        if (lines.length > 0) {

            for (var y =0; y < lines.length; y++) {

                var l = lines;

                if (l.length != 0) {

                    if (l[0] !== "") {

                        var names = l.split(";");

                        if (names.length == 2) {

                            oNames.push(names[0]);

                            nNames.push(names[1]);

                        }

                    }

                } // l.length

            } // for

            return true;

        } // lines.length

        return false;

    }

    //////// FUNCTIONS ////////////

    function myFindFile(myFilePath) {

        var myScriptFile = myGetScriptPath();

        var myScriptFile = File(myScriptFile);

        var myScriptFolder = myScriptFile.path;

        myFilePath = myScriptFolder + "/" + myFilePath;

        if(File(myFilePath).exists == false) {

            //Display a dialog.

            myFilePath = File.openDialog(infos.findFile);

        }

        // else alert("ok");

        return myFilePath;

    }

    function myGetScriptPath() {

        try {

            myFile = app.activeScript;

        }

        catch(myError){

            myFile = myError.fileName;

        }

        return myFile;

    }

}());

I hope this helped,

Swo.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Wow!  Another code and more work for my saturday!

I'm going to work on it too and I'll give you comments soon!

Thanks a lot!

… and have a nice WE too! 

(^/)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Michel -- Maybe you should tell us what you want to achieve. Rename layers, using the text file, so that Layer_1 is renamed Layer_12, etc.?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 05, 2016 Nov 05, 2016

Copy link to clipboard

Copied

Hi Peter,

The idea is, using a list of concordances, as: old layer name / new layer name, joined as a .txt file [the list could be done in Excel and exported as I said], to change the layer names in ID using this list.

Some layer names have to be changed (concordance in the .txt file) but certains not.

I've already manipulated external lists as apply a char style to a list of words or make an index from a list of words.

I've never done it in this kind of situation.

In fact, I'm really interested by the way we could play it. I've actually 3 scripts, mine doesn't work!

Uwe's one seems to work fine! As I said, the most important for me is to study and understand codes and truly be able to replicate them.

I don't search somebody to write scripts for me! I just would like help to tell me why I'm wrong and learn more in right directions!

[JS] is really awesome and I think we are very lucky to have here true JS masters!

(^/)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Ok, then here's yet another one. The code you wrote and showed above (in comment 2) is very verbose. All you need is this:

myList = ["Layer_1;Layer_12","Layer_2;Layer_23"];

myLayers = app.activeDocument.layers;

for (L = 0; L < myLayers.length; L++) {

  parts = myList.split (';');

  myLayers.item(parts[0]).name = myLayers.parts[1];

}

Peter

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Hi Peter,

I've an error on "parts"! 

Capture d’écran 2016-11-06 à 11.03.25.png

(^/)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Hi Obi-wan,

About error handling:
What could go wrong?

Customer errors:

1. The layer name provided as "old name" does not match existing names.

2. The layer name provided as "old name" is provided more than one time.

3. The "new name" is provided more than one time in the provided list.

To tackle issue 1 and 2 the incoming list has to be pre-processed and should be matched against the existing names.
Same with issue 3, pre-process the incoming list. Write an error report and contact the customer before proceeding.

Structure problem (no customer error):

3. The "new name" is already in use with a layer that the loop did not come accross yet.

How to tackle the structure problem?
Before doing anything according to the provided list, rename all layers with e.g. a numbering scheme.

doc.layers.name = n+uniqueSeparator+doc.layers.name;


Then you could start renaming using the provided list.

Log all layers that are not renamed according to the list.

Remove the numbering scheme for all layers not provided by the list.

Below an example.

"Layer_3", "Background_1 and "Background_2" should not be renamed and therefore are not provided by the list:

var notUsedCharacterInNames = ";"

var listArray =

    [

        "Layer_1"+notUsedCharacterInNames+"Layer_5",

        "Layer_2"+notUsedCharacterInNames+"Layer_4",

        "Layer_4"+notUsedCharacterInNames+"Layer_2",

        "Layer_5"+notUsedCharacterInNames+"Layer_1"

    ];

1-BeforeRenaming.png

After renaming:

2-AfterRenaming.png

Regards,

Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

@Uwe: Thanks for this interesting exemple where renaming "somethings" doesn't always means completely differents names.

@Obi:

About your first question: going from text file to [["oName 1", "nName 1"], ["oName 2", "nName 2"], ...]

it would be a function with only one array for names (allNames):

function oneListNames() {

    // Open the file for reading

    myListFile.open("r");

    var text = myListFile.read();

    var lines = text.split("\n");

    if (lines.length > 0) {

        for (var y =0; y < lines.length; y++) {

            var l = lines;

            if (l.length != 0) {

                if (l[0] !== "" && l[1] !== "") {

                    var names = l.split(";");

                    if (names.length > 1) {

                        allNames.push([names[0], names[1]]);

                    }

                }

            }

        }

        return true;

    }

    return false;

}

Swo

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Uwe and Swo,

Very cool answers for me! Thanks a lot!

It's a very good learning for me to study how you think and how you write your own code!

I'm going to study more all about "array" and "error" use.

@Swo, I'm very interested by your approach about the external .txt file call! I'm going to study it too!

Maybe more questions on this point later!

(^/) 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Yes Peter,

but I think there should be some error handling.

Maybe "Layer_12" is already used as name in the stack of layers, so "Layer_1" cannot be renamed?

Regards,
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

I think we need a double loop as I did!

Actually, as I've corrected and inserted an "if", the code corrects first, the top layer, then next in the Layers panel.

(^/)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

No, you don't need nested loops. Line 5 in my code should be as follows:

myLayers.item(parts[0]).name = parts[1];

And sure, error handling should be handled, but all that error-handling obscures the basics of what you're trying to illustrate.

P.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Peter,

I totally like your code!

I didn't know the "parts" syntax and the way you thought the loop is great!

… but I've an error even if the script correctly renames the layers! …

Do I need to write a try…catch to delete it?

Capture d’écran 2016-11-06 à 17.55.13.png

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Ugh... In line 4, 'myLayers.length' should be 'myList.length'.

P.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Yeap!

myList = ["Layer_1;Layer_12","Layer_2;Layer_23"]; 

myLayers = app.activeDocument.layers; 

for (L = 0; L < myList.length; L++) { 

    parts = myList.split (';');

    myLayers.item(parts[0]).name = parts[1];

}

Previously, I've corrected as:

myList = ["Layer_1;Layer_12","Layer_2;Layer_23"]; 

myLayers = app.activeDocument.layers; 

for (L = 0; L < myLayers.length; L++) { 

        try

        {

            parts = myList.split (';');

            myLayers.item(parts[0]).name = parts[1];

        }

        catch(myError){} 

}

… and I've no error!

So, my big questions:

In a script writing, is it dangerous to systematically "mask" real errors with a try…catch (as I did)?

and:

do I need to systematically find why there's an error and where it is to avoid awful problems in the future?

(^/) 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

As you said : using

catch(myError){}  

doesn't mean there isn't any error, but that you "mask" them.

There's a possible error if the new name is already used for a layer.

I usually don't hide error messages, but I try to thinks or test untill there's no more errors.

Uwe showed me in his example that I missed some possible errors.

When you'll test yours scripts with users, they'll find new errors you didn't thought about :S

(That's the way when working with creative people)

If you hide errors, how about in six month, or a year later... You'll use this script, and won't understand why it doesn't produce any result

...because you'll have forgotten about hiding errors.

If you don't have yet a solution, at least add an alert. Later, when you'll have more time, you'll be able to debug or complete the script.

I use this as a reminder, and because users feel betrayed or frightened (sort of) when you give a script that "doesn't work" or give cryptic error messages (inDesign error messages).

Just my 2 cents about this,

Swo.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 06, 2016 Nov 06, 2016

Copy link to clipboard

Copied

Hi Obi-wan,

and be aware, that a provided list should always be prepocessed.
Otherwise some strange things could happen. 🙂

A "normal" user cannot:

1. Use a name with an empty string.

A script can set it. A list can provide it.

doc.activeLayer.name = "";

1-Layer-Name-EmptyString.png

Here the UI is not sure what to tell:

2-Layer-Name-EmptyString.png

2. Use names with white space only.

A script can set it. A list can provide it.

doc.activeLayer.name = "          ";

2-Layer-Name-WhitespaceOnly.png

1-Layer-Name-WhitespaceOnly.png

3. Use names with trailing white space.

A script can set it. A list can provide it.

doc.activeLayer.name = "    trailing white space      ";

2-Layer-Name-TrailingWhiteSpace.png

1-Layer-Name-TrailingWhiteSpace.png

Hm:

"Ils doivent envisager qu’une grande responsabilité est la suite inséparable d’un grand pouvoir."
And also: "With Great Power Comes Great Responsibility".

Regards,
Uwe

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines