Skip to main content
Participant
December 26, 2018
Question

SortParagraphs.jsx Modification to Numerically Order (and Alphabetically Order)

  • December 26, 2018
  • 1 reply
  • 1238 views

I'd like a small modification of SortParagraphs.jsx (that is included with InDesign), so it will produce alphabetically ordered results with numerals that include colons in references.

For example, currently the script will return results in this order:

Abc 11:2

Abc 1:2

(Somehow the colon is causing the problem.)

It needs to be:

Abc 1:2

Abc 11:2

If I go to a site like sortmylist.com it orders the results as hoped for with only one click. Can you tweak SortParagraphs.jsx? Or does it need a newly designed script?

(I am using CS5 but checked the script with the same name for CS6 and didn't see any differences. Maybe the most recent one has changes?)

//SortParagraphs.jsx

//An InDesign CS5 JavaScript

/* 

@@@BUILDINFO@@@ "SortParagraphs.jsx" 3.0.0 15 December 2009

*/

//Sorts the paragraphs in the selection in alphabetical order.

//

//For more on InDesign scripting, go to http://www.adobe.com/products/indesign/scripting/index.html

//or visit the InDesign Scripting User to User forum at http://www.adobeforums.com

//

main();

function main(){

//Make certain that user interaction (display of dialogs, etc.) is turned on.

app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;

if(app.documents.length != 0){

if(app.selection.length > 0){

switch(app.selection[0].constructor.name){

case "Text":

case "TextColumn":

case "TextFrame":

if(app.selection[0].paragraphs.length > 1){

myDisplayDialog();

}

else{

alert("Please select at least two paragraphs of text (or a text frame) try again.");

}

break;

case "Cell":

case "Row":

case "Table":

case "Column":

alert("This script cannot sort table rows, columns, or cells.\rTry converting the table to text, sorting \rthe text, and then converting back to a table.");

break;

default:

alert("Please select at least two paragraphs of text (or a text frame) try again.");

}

}

else{

alert("Please select at least two paragraphs of text (or a text frame) try again.");

}

}

else{

alert("No documents are open. Please open a document and try again.");

}

}

function myDisplayDialog(){

var myDialog = app.dialogs.add({name:"Sort Options"});

with(myDialog.dialogColumns.add()){

with(dialogRows.add()){

with(dialogColumns.add()){

staticTexts.add({staticLabel:"Sort Method:"});

}

with(dialogColumns.add()){

var mySortMethodDropdown = dropdowns.add({stringList:["Ignore Formatting (faster)", "Retain Formatting (slower)"], selectedIndex:0});

}

}

with(dialogRows.add()){

var myIgnoreSpacesCheckbox = checkboxControls.add({staticLabel:"Ignore Spaces", checkedState:true});

}

with(dialogRows.add()){

var myReverseSortCheckbox = checkboxControls.add({staticLabel:"Reverse Sort", checkedState:false});

}

}

var myResult = myDialog.show();

if(myResult == true){

var mySortMethod = mySortMethodDropdown.selectedIndex;

var myIgnoreSpaces = myIgnoreSpacesCheckbox.checkedState;

var myReverseSort = myReverseSortCheckbox.checkedState;

myDialog.destroy();

mySortParagraphs(mySortMethod, myIgnoreSpaces, myReverseSort);

}

else{

myDialog.destroy();

}

}

function mySortParagraphs(mySortMethod, myIgnoreSpaces, myReverseSort){

var myParagraphs, myCleanUp, myItemMoved, myCounter, myStringA, myStringB;

if(app.selection[0].constructor.name == "TextFrame"){

myParagraphs = app.selection[0].paragraphs;

}

else{

if((app.selection[0].contents == "")||(app.selection[0].paragraphs.length <= 1)){

myParagraphs = app.selection[0].parentTextFrames[0].paragraphs;

}

else{

myParagraphs = app.selection[0].paragraphs;

}

}

//If the last paragraph in the selection is the last paragraph of the story,

//and if the last paragraph does not end in a carriage return character,

//then add a carriage return character at the end of the last paragraph.

if(myParagraphs.item(0).parentStory.insertionPoints.item(-1).index == myParagraphs.item(-1).insertionPoints.item(-1).index){

myParagraphs.item(-1).insertionPoints.item(-1).contents = "\r";

myCleanUp = true;

}

else{

myCleanUp = false;

}

//Switch statement added here to allow for others to add custom sort methods. To do this,

//add another item to the dropdown menu in the dialog function, then add a corresponding

//case in the switch below.

switch(mySortMethod){

case 0:

//JavaScript sort:

var myString = "";

var myArray = myParagraphs.everyItem().contents;

if(myIgnoreSpaces != true){

if(myReverseSort != true){

myArray = myArray.sort(mySort);

}

else{

myArray = myArray.sort(myReverseSort);

}

}

else{

if(myReverseSort != true){

myArray = myArray.sort(mySortIgnoringSpaces);

}

else{

myArray = myArray.sort(myReverseSortIgnoringSpaces);

}

}

for(myCounter = 0; myCounter < myArray.length; myCounter ++){

myString += myArray[myCounter];

}

var myStartCharacter = myParagraphs.item(0).characters.item(0);

var myEndCharacter = myParagraphs.item(-1).characters.item(-1);

var myText = myParagraphs.item(0).parentStory.texts.itemByRange(myStartCharacter, myEndCharacter);

//Replace the contents with the string;

myText.contents = myString;

break;

case 1:

//Simple bubble sort (demonstrates the paragraph.move() method):

if(myReverseSort != true){

do{

myItemMoved = false;

myCounter = 0;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter+1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA > myStringB){

myParagraphs.item(myCounter).move(LocationOptions.after, myParagraphs.item(myCounter+1));

myItemMoved = true;

}

myCounter ++;

}while (myCounter < myParagraphs.length-1);

myCounter = myParagraphs.length-1;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter-1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA < myStringB){

myParagraphs.item(myCounter).move(LocationOptions.before, myParagraphs.item(myCounter-1));

myItemMoved = true;

}

myCounter --;

}while(myCounter > 1);

}while(myItemMoved != false);

}

else{

do{

myItemMoved = false;

myCounter = 0;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter+1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA < myStringB){

myParagraphs.item(myCounter).move(LocationOptions.after, myParagraphs.item(myCounter+1));

myItemMoved = true;

}

myCounter ++;

}while (myCounter < myParagraphs.length-1);

myCounter = myParagraphs.length-1;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter-1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA > myStringB){

myParagraphs.item(myCounter).move(LocationOptions.before, myParagraphs.item(myCounter-1));

myItemMoved = true;

}

myCounter --;

}while(myCounter > 1);

}while(myItemMoved != false);

}

break;

//User-defined cases can be added here.

}

//If we added a return at the end of the story, we should now

//remove the extra return at the end of the story.

if(myCleanUp == true){

myParagraphs.item(0).parentStory.characters.item(-1).remove();

}

}

function mySort(a, b){

a = a.toLowerCase();

b = b.toLowerCase();

if(a > b){

return 1;

}

if(a < b){

return -1;

}

return 0;

}

function mySortIgnoringSpaces(a, b){

var myRegExp = /\s/gi;

a = a.toLowerCase().replace(myRegExp, "");

b = b.toLowerCase().replace(myRegExp, "");

if(a > b){

return 1;

}

if(a < b){

return -1;

}

return 0;

}

function myReverseSort(a, b){

a = a.toLowerCase();

b = b.toLowerCase();

if(a > b){

return -1;

}

if(a < b){

return 1;

}

return 0;

}

function myReverseSortIgnoringSpaces(a, b){

var myRegExp = /\s/gi;

a = a.toLowerCase().replace(myRegExp, "");

b = b.toLowerCase().replace(myRegExp, "");

if(a > b){

return -1;

}

if(a < b){

return 1;

}

return 0;

}

This topic has been closed for replies.

1 reply

Community Expert
December 26, 2018

Hello,

In order to modify the sort result you need to change the definition of the methods passed as argument to the sort method, i.e. mySortIgnoringSpaces, mySortIgnoringSpaces, myReverseSortIgnoringSpaces

Now the result that the script gives is fine as per the normal ASCII sequence, in this sequence : comes after 1 and hence ABC 11:2 would come before ABC 1:2. Now as per you requirement that the sequence should be

ABC 1:2

ABC 11:2

You are trying to half sort the paragraph using alphabetical order and the other half using numerical order, but even in this case how would you deal with special characters like :,[ etc are they to be ignored. What happens when a number is compared with a character etc there are a no. of use cases that you need to think of to devise your sorting algorithm. Once you are clear how all the use cases need to be handled you can change the definitions of the above method and things should work.

I looked at the code of the website you mentioned and it seems it is escaping the string before comparing it, doing that in one of the methods i mentioned above gives the result that you want but i have not checked all the use cases. You can try to see if works for all your needs. Change the method definition in your code to the following and try

function mySortIgnoringSpaces(a, b){

     var myRegExp = /\s/gi;

     a = escape(a.toLowerCase().replace(myRegExp, ""));

     b = escape(b.toLowerCase().replace(myRegExp, ""));

     if(a > b){

          return 1;

     }

     if(a < b){

          return -1;

     }

     return 0;

}

If it works, you can make the same change in all the other functions. I just called the escape method on the return of replace method.

-Manan

-Manan
UIHMXAuthor
Participant
December 27, 2018

Thanks for your input.

I couldn't get your code to work but as I thought about your conceptual basis, I tried to figure out the definition, or define the parameters. The full GREP pattern that can isolate the data which "falls through the cracks" seems to be:

^(\d |\u).+?([.]| )\d([:]| ).+?$

So I thought we could add a "0" to the numbers less than 10 as a prefix tag.

e.g:

Before: Abc 1:2

After: Abc 01:2

That seems to order all the data numerically (by my test) using the original SortParagraphs.jsx script.

e.g:

Before:

Abc 11:2

Abc 1:2

After:

Abc 01:2

Abc 11:2

Then simply remove that zero tag after the sorting.

e.g:

Abc 1:2

Abc 11:2

Community Expert
December 27, 2018

If your data will always be in the format i.e.

alphabets space digits:digits

Then you can try the following code. The idea is to separate the content into the alphabet and numeric parts. Then sort using the alphabets, if the alphabet part is equal use the numeric part to decide the order.

//SortParagraphs.jsx

//An InDesign JavaScript

/* 

@@@BUILDINFO@@@ "SortParagraphs.jsx" 3.0.0 15 December 2009

*/

//Sorts the paragraphs in the selection in alphabetical order.

//

//For more on InDesign/InCopy scripting see the documentation included in the Scripting SDK

//available at http://www.adobe.com/devnet/indesign/sdk.html

//or visit the InDesign Scripting User to User forum at http://www.adobeforums.com

//

main();

function main(){

//Make certain that user interaction (display of dialogs, etc.) is turned on.

app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;

if(app.documents.length != 0){

if(app.selection.length > 0){

switch(app.selection[0].constructor.name){

case "Text":

case "TextColumn":

case "TextFrame":

if(app.selection[0].paragraphs.length > 1){

myDisplayDialog();

}

else{

alert("Please select at least two paragraphs of text (or a text frame) try again.");

}

break;

case "Cell":

case "Row":

case "Table":

case "Column":

alert("This script cannot sort table rows, columns, or cells.\rTry converting the table to text, sorting \rthe text, and then converting back to a table.");

break;

default:

alert("Please select at least two paragraphs of text (or a text frame) try again.");

}

}

else{

alert("Please select at least two paragraphs of text (or a text frame) try again.");

}

}

else{

alert("No documents are open. Please open a document and try again.");

}

}

function myDisplayDialog(){

var myDialog = app.dialogs.add({name:"Sort Options"});

with(myDialog.dialogColumns.add()){

with(dialogRows.add()){

with(dialogColumns.add()){

staticTexts.add({staticLabel:"Sort Method:"});

}

with(dialogColumns.add()){

var mySortMethodDropdown = dropdowns.add({stringList:["Ignore Formatting (faster)", "Retain Formatting (slower)"], selectedIndex:0});

}

}

with(dialogRows.add()){

var myIgnoreSpacesCheckbox = checkboxControls.add({staticLabel:"Ignore Spaces", checkedState:true});

}

with(dialogRows.add()){

var myReverseSortCheckbox = checkboxControls.add({staticLabel:"Reverse Sort", checkedState:false});

}

}

var myResult = myDialog.show();

if(myResult == true){

var mySortMethod = mySortMethodDropdown.selectedIndex;

var myIgnoreSpaces = myIgnoreSpacesCheckbox.checkedState;

var myReverseSort = myReverseSortCheckbox.checkedState;

myDialog.destroy();

mySortParagraphs(mySortMethod, myIgnoreSpaces, myReverseSort);

}

else{

myDialog.destroy();

}

}

function mySortParagraphs(mySortMethod, myIgnoreSpaces, myReverseSort){

var myParagraphs, myCleanUp, myItemMoved, myCounter, myStringA, myStringB;

if(app.selection[0].constructor.name == "TextFrame"){

myParagraphs = app.selection[0].paragraphs;

}

else{

if((app.selection[0].contents == "")||(app.selection[0].paragraphs.length <= 1)){

myParagraphs = app.selection[0].parentTextFrames[0].paragraphs;

}

else{

myParagraphs = app.selection[0].paragraphs;

}

}

//If the last paragraph in the selection is the last paragraph of the story,

//and if the last paragraph does not end in a carriage return character,

//then add a carriage return character at the end of the last paragraph.

if(myParagraphs.item(0).parentStory.insertionPoints.item(-1).index == myParagraphs.item(-1).insertionPoints.item(-1).index){

myParagraphs.item(-1).insertionPoints.item(-1).contents = "\r";

myCleanUp = true;

}

else{

myCleanUp = false;

}

//Switch statement added here to allow for others to add custom sort methods. To do this,

//add another item to the dropdown menu in the dialog function, then add a corresponding

//case in the switch below.

switch(mySortMethod){

case 0:

//JavaScript sort:

var myString = "";

var myArray = myParagraphs.everyItem().contents;

if(myIgnoreSpaces != true){

if(myReverseSort != true){

myArray = myArray.sort(mySort);

}

else{

myArray = myArray.sort(myReverseSort);

}

}

else{

if(myReverseSort != true){

myArray = myArray.sort(mySortIgnoringSpaces);

}

else{

myArray = myArray.sort(myReverseSortIgnoringSpaces);

}

}

for(myCounter = 0; myCounter < myArray.length; myCounter ++){

myString += myArray[myCounter];

}

var myStartCharacter = myParagraphs.item(0).characters.item(0);

var myEndCharacter = myParagraphs.item(-1).characters.item(-1);

var myText = myParagraphs.item(0).parentStory.texts.itemByRange(myStartCharacter, myEndCharacter);

//Replace the contents with the string;

myText.contents = myString;

break;

case 1:

//Simple bubble sort (demonstrates the paragraph.move() method):

if(myReverseSort != true){

do{

myItemMoved = false;

myCounter = 0;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter+1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA > myStringB){

myParagraphs.item(myCounter).move(LocationOptions.after, myParagraphs.item(myCounter+1));

myItemMoved = true;

}

myCounter ++;

}while (myCounter < myParagraphs.length-1);

myCounter = myParagraphs.length-1;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter-1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA < myStringB){

myParagraphs.item(myCounter).move(LocationOptions.before, myParagraphs.item(myCounter-1));

myItemMoved = true;

}

myCounter --;

}while(myCounter > 1);

}while(myItemMoved != false);

}

else{

do{

myItemMoved = false;

myCounter = 0;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter+1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA < myStringB){

myParagraphs.item(myCounter).move(LocationOptions.after, myParagraphs.item(myCounter+1));

myItemMoved = true;

}

myCounter ++;

}while (myCounter < myParagraphs.length-1);

myCounter = myParagraphs.length-1;

do{

myStringA = myParagraphs.item(myCounter).contents.toLowerCase();

myStringB = myParagraphs.item(myCounter-1).contents.toLowerCase();

if(myIgnoreSpaces == true){

myStringA = myStringA.replace(/\s/gi, "");

myStringB = myStringB.replace(/\s/gi, "");

}

if(myStringA > myStringB){

myParagraphs.item(myCounter).move(LocationOptions.before, myParagraphs.item(myCounter-1));

myItemMoved = true;

}

myCounter --;

}while(myCounter > 1);

}while(myItemMoved != false);

}

break;

//User-defined cases can be added here.

}

//If we added a return at the end of the story, we should now

//remove the extra return at the end of the story.

if(myCleanUp == true){

myParagraphs.item(0).parentStory.characters.item(-1).remove();

}

}

function mySort(a, b){

a = a.toLowerCase();

b = b.toLowerCase();

var reg = a.match(/^([a-z]+?\s+)(\d+?):(\d+)\s*$/)

var alphaOne = reg[1]

var numOne = parseInt(reg[2]+reg[3])

reg = b.match(/^([a-z]+?\s+)(\d+?):(\d+)\s*$/)

var alphaTwo = reg[1]

var numTwo = parseInt(reg[2]+reg[3])

if(alphaOne > alphaTwo){

return 1;

}

if(alphaOne < alphaTwo){

return -1;

}

if(numOne > numTwo)

return 1

if(numOne < numTwo)

return -1

return 0;

}

function mySortIgnoringSpaces(a, b){

var myRegExp = /\s/gi;

a = a.toLowerCase().replace(myRegExp, "");

b = b.toLowerCase().replace(myRegExp, "");

var reg = a.match(/^([a-z]+?)(\d+?):(\d+)\s*$/)

var alphaOne = reg[1]

var numOne = parseInt(reg[2]+reg[3])

reg = b.match(/^([a-z]+?)(\d+?):(\d+)\s*$/)

var alphaTwo = reg[1]

var numTwo = parseInt(reg[2]+reg[3])

if(alphaOne > alphaTwo){

return 1;

}

if(alphaOne < alphaTwo){

return -1;

}

if(numOne > numTwo)

return 1

if(numOne < numTwo)

return -1

return 0;

}

function myReverseSort(a, b){

a = a.toLowerCase();

b = b.toLowerCase();

if(a > b){

return -1;

}

if(a < b){

return 1;

}

return 0;

}

function myReverseSortIgnoringSpaces(a, b){

var myRegExp = /\s/gi;

a = a.toLowerCase().replace(myRegExp, "");

b = b.toLowerCase().replace(myRegExp, "");

var reg = a.match(/^([a-z]+?)(\d+?):(\d+)\s*$/)

var alphaOne = reg[1]

var numOne = parseInt(reg[2]+reg[3])

reg = b.match(/^([a-z]+?)(\d+?):(\d+)\s*$/)

var alphaTwo = reg[1]

var numTwo = parseInt(reg[2]+reg[3])

if(alphaOne < alphaTwo){

return 1;

}

if(alphaOne > alphaTwo){

return -1;

}

if(numOne < numTwo)

return 1

if(numOne > numTwo)

return -1

return 0;

}

-Manan