Sort / Order Selected Layers (Alphabetically)?

Explorer ,
Feb 14, 2018 Feb 14, 2018

Copy link to clipboard

Copied

Hi,

I'm looking for a script to order selected layers alphabetically. I'm familiar with  Adobe Photoshop Scripts | Trevor Morris Photographics​, but am looking for constraint to selection. I've tried using this script: get array of selected layers in photoshop via extendscript · GitHub in combination, however, I've hit a wall.

Thanks,

Tom

TOPICS
Actions and scripting

Views

13.6K

Likes

translate

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

correct answers 1 Correct Answer

Adobe Community Professional , Feb 15, 2018 Feb 15, 2018
I reproduced the error ). This should workvar idx_shift = 0;  try { activeDocument.backgroundLayer } catch (e) { idx_shift = 1; }  while (1)      {      var list = get_selected_layers();        if (!list) { alert("No selected layers"); break; }        var list2 = new Array();      for (var i in list) list2.push(list);        function cmp(a, b)           {          var x = expand_numbers(a[0]);          var y = expand_numbers(b[0]);            if (x > y) return -1;          if (x < y) return 1;  ...

Likes

translate

Translate

Translate
Adobe Community Professional ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Try this code )

while (1)

    {

    var list = get_selected_layers();

    if (!list) { alert("No selected layers"); break; }

    var list2 = new Array();

    for (var i in list) list2.push(list);

    function cmp(a, b)

        {

        var x = expand_numbers(a[0]);

        var y = expand_numbers(b[0]);

        if (x > y) return -1;

        if (x < y) return 1;

        return 0;

        }

    list.sort(cmp);

    var cont = false;

    for (var i = 0; i < list.length; i++)

        {

        if (list2[1] != list[1])

            {

            set_layers_index(list2[1], list[1]);

            cont = true;

            break;

            }

        }

    if (!cont) break;

    }

alert("Done!");

//////////////////////////////////////////////////////////////////

function set_layers_index(cur_idx, new_idx)

    {

    try {

        var d = new ActionDescriptor();

        var r1 = new ActionReference();

        r1.putIndex( charIDToTypeID( "Lyr " ), cur_idx);

        var r2 = new ActionReference();

        r2.putIndex( charIDToTypeID( "Lyr " ), new_idx );

        d.putReference( charIDToTypeID( "null" ), r1 );

        d.putReference( charIDToTypeID( "T   " ), r2 );

        executeAction( charIDToTypeID( "move" ), d, DialogModes.NO );

        }

    catch (e) { alert(e);  }

    }

//////////////////////////////////////////////////////////////////

function get_selected_layers()

    {

    try {

        var r = new ActionReference();   

        r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));  

        r.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));

        var list = executeActionGet(r).getList(stringIDToTypeID("targetLayers"));

        var n = 0;

        try { activeDocument.backgroundLayer } catch (e) { n = 1; }

        var len = list.count;

   

        var selected_layers = new Array();

   

        for (var i = 0; i < len; i++)

            {

            try

                {

                var idx = list.getReference(i).getIndex() + n;

                var r = new ActionReference();

                r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("name"));  

                r.putIndex( charIDToTypeID( "Lyr " ), idx);

                var ret = executeActionGet(r).getString(stringIDToTypeID("name"));

                selected_layers.push([executeActionGet(r).getString(stringIDToTypeID("name")), idx]);

                }

            catch (e) { alert(e); return null; }

            }

   

        return selected_layers;

        }

    catch (e) { return null; }

    }

////////////////////////////////////////////////////////////////////////////////////////////

function expand_numbers(s, max_digit)

    {

    if (max_digit == undefined) max_digit = 10;   

    var digit_cnt;

    s = s.toString();

   

    var len = s.length;

    var s1 = "";

    for (var i = len-1; i >= 0; i--)

        {

        var c = s.charAt(i);

        if (c >= "0" && c <="9")

            {

            if (digit_cnt == undefined)    

                digit_cnt = 1;

            else                       

                ++digit_cnt;

            s1 += c;

            }

        else if (digit_cnt != undefined)

            {

            if (digit_cnt < max_digit) for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";

            s1 += c;

            digit_cnt = undefined;

            }

        else

            {

            s1 += c;

            }

        }

    if (digit_cnt != undefined && digit_cnt < max_digit)

        for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";

    len = s1.length;

    var ret = "";

    for (var i = len-1; i >= 0; i--) ret += s1.charAt(i);

    ret = ret.toUpperCase();

    return ret;

    }

Likes

translate

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
Explorer ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Thanks!

I ran the script and all but one of the selected layers got grouped into the group above them. I undid that and placed the group below all of the selected layers and ran again. Got a script alert: Error: General Photoshop error occurred. This functionality may not be available in this version of Photoshop.

- The command "Move" is not currently available.

I click OK or the x button and a new script alert appears (saying the same thing). I cannot exit Script Alerts...

Likes

translate

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
Adobe Community Professional ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

Please post the screenshot of your layers structures and their selection. What version of Photoshop are you running?

upd. change

catch (e) { alert(e);  }

to

catch (e) { alert(e); throw(e);  }

Likes

translate

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
Adobe Community Professional ,
Feb 15, 2018 Feb 15, 2018

Copy link to clipboard

Copied

I reproduced the error ).

This should work

var idx_shift = 0; 

try { activeDocument.backgroundLayer } catch (e) { idx_shift = 1; } 

while (1) 

    { 

    var list = get_selected_layers(); 

 

    if (!list) { alert("No selected layers"); break; } 

 

    var list2 = new Array(); 

    for (var i in list) list2.push(list); 

 

    function cmp(a, b)  

        { 

        var x = expand_numbers(a[0]); 

        var y = expand_numbers(b[0]); 

 

        if (x > y) return -1; 

        if (x < y) return 1; 

        return 0; 

        } 

 

    list.sort(cmp); 

    var cont = false; 

 

    for (var i = 0; i < list.length; i++) 

        { 

        if (list2[1] != list[1])  

            { 

            set_layers_index(list2[1], list[1]); 

            cont = true; 

            break; 

            } 

        } 

 

    if (!cont) break; 

    } 

 

alert("Done!"); 

 

////////////////////////////////////////////////////////////////// 

function set_layers_index(cur_idx, new_idx) 

    { 

    try { 

        var d = new ActionDescriptor(); 

 

        var r1 = new ActionReference(); 

        r1.putIndex( charIDToTypeID( "Lyr " ), cur_idx); 

 

        var r2 = new ActionReference(); 

        r2.putIndex( charIDToTypeID( "Lyr " ), new_idx-idx_shift ); 

 

        d.putReference( charIDToTypeID( "null" ), r1 ); 

        d.putReference( charIDToTypeID( "T   " ), r2 ); 

 

        executeAction( charIDToTypeID( "move" ), d, DialogModes.NO ); 

        } 

    catch (e) { alert(e); throw(e); } 

    } 

 

////////////////////////////////////////////////////////////////// 

function get_selected_layers() 

    { 

    try { 

        var r = new ActionReference();     

        r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));    

        r.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); 

 

        var list = executeActionGet(r).getList(stringIDToTypeID("targetLayers")); 

 

        var len = list.count; 

     

        var selected_layers = new Array(); 

     

        for (var i = 0; i < len; i++) 

            { 

            try  

                { 

                var idx = list.getReference(i).getIndex() + idx_shift; 

 

                var r = new ActionReference(); 

                r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("name"));    

                r.putIndex( charIDToTypeID( "Lyr " ), idx); 

 

                var ret = executeActionGet(r).getString(stringIDToTypeID("name")); 

 

                selected_layers.push([executeActionGet(r).getString(stringIDToTypeID("name")), idx]); 

                } 

 

            catch (e) { alert(e); return null; } 

            } 

     

        return selected_layers; 

        } 

 

    catch (e) { return null; } 

    } 

 

//////////////////////////////////////////////////////////////////////////////////////////// 

function expand_numbers(s, max_digit) 

    { 

    if (max_digit == undefined) max_digit = 10;     

 

    var digit_cnt; 

 

    s = s.toString(); 

     

    var len = s.length; 

 

    var s1 = ""; 

 

    for (var i = len-1; i >= 0; i--)  

        { 

        var c = s.charAt(i); 

 

        if (c >= "0" && c <="9") 

            { 

            if (digit_cnt == undefined)      

                digit_cnt = 1; 

            else                         

                ++digit_cnt; 

 

            s1 += c; 

            } 

        else if (digit_cnt != undefined)  

            { 

            if (digit_cnt < max_digit) for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0"; 

 

            s1 += c; 

            digit_cnt = undefined; 

            } 

        else 

            { 

            s1 += c; 

            } 

        } 

 

    if (digit_cnt != undefined && digit_cnt < max_digit) 

        for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0"; 

 

    len = s1.length; 

 

    var ret = ""; 

 

    for (var i = len-1; i >= 0; i--) ret += s1.charAt(i); 

 

    ret = ret.toUpperCase(); 

 

    return ret; 

    } 

Likes

translate

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
Explorer ,
Feb 21, 2018 Feb 21, 2018

Copy link to clipboard

Copied

Amazing! Thank you very much--I really appreciate it.

Likes

translate

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
Adobe Community Professional ,
Feb 26, 2018 Feb 26, 2018

Copy link to clipboard

Copied

I compared performance of our scripts and found the more selected layers the longer time your script needs to sort all of them. Only up to 10 selected layers yours is a little faster - here is comparision in seconds (layers number / yours / mine):

10     /     0.12     /     0.17

20     /     2.20     /     0.38

30     /     8.00     /     0.55

40     /     25.4     /     0.80

50     /     57.5     /     1.00

It also does not handle layers with same name like '1' and '1', 'Layer 0' and 'Layer 0' or 'a' and 'A'. It's looping endlessly itself...

Likes

translate

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
Adobe Community Professional ,
Feb 26, 2018 Feb 26, 2018

Copy link to clipboard

Copied

Here is new version whithout "while" cycle )

var idx_shift = 0;   

try { activeDocument.backgroundLayer } catch (e) { idx_shift = 1; }   

var list = get_selected_layers();   

if (list)

    {

    var orig_idx = new Array();   

    for (var i in list) orig_idx.push(list[1]);   

    function cmp(a, b)    

        {   

        var x = expand_numbers(a[0]);   

        var y = expand_numbers(b[0]);   

        if (x > y) return -1;   

        if (x < y) return 1;   

        return 0;   

        }   

    list.sort(cmp);   

    for (var i = list.length-1; i >= 0; i--) set_layers_index(list[2], orig_idx);   

    }

alert("Done!");   

   

//////////////////////////////////////////////////////////////////   

function set_layers_index(id, idx)   

    {   

    try {   

        var d = new ActionDescriptor();   

   

        var r1 = new ActionReference();   

        r1.putIdentifier( charIDToTypeID( "Lyr " ), id);   

   

        var r2 = new ActionReference();   

        r2.putIndex( charIDToTypeID( "Lyr " ), idx-idx_shift );   

   

        d.putReference( charIDToTypeID( "null" ), r1 );   

        d.putReference( charIDToTypeID( "T   " ), r2 );   

   

        executeAction( charIDToTypeID( "move" ), d, DialogModes.NO );   

        }   

    catch (e) { alert(e); throw(e); }   

    }   

   

//////////////////////////////////////////////////////////////////   

function get_selected_layers()   

    {   

    try {   

        var r = new ActionReference();       

        r.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));      

        r.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));   

   

        var list = executeActionGet(r).getList(stringIDToTypeID("targetLayers"));   

   

        var len = list.count;   

       

        var selected_layers = new Array();   

       

        for (var i = 0; i < len; i++)   

            {   

            try    

                {   

                var idx = list.getReference(i).getIndex() + idx_shift;   

   

                var r = new ActionReference();   

                r.putIndex( charIDToTypeID( "Lyr " ), idx);   

                var ret = executeActionGet(r);

   

                selected_layers.push([ret.getString(stringIDToTypeID("name")), idx, ret.getString(stringIDToTypeID("layerID"))]);   

                }   

   

            catch (e) { alert(e); return null; }   

            }   

       

        return selected_layers;   

        }   

   

    catch (e) { return null; }   

    }   

   

////////////////////////////////////////////////////////////////////////////////////////////   

function expand_numbers(s, max_digit)   

    {   

    if (max_digit == undefined) max_digit = 10;       

   

    var digit_cnt;   

   

    s = s.toString();   

       

    var len = s.length;   

   

    var s1 = "";   

   

    for (var i = len-1; i >= 0; i--)    

        {   

        var c = s.charAt(i);   

   

        if (c >= "0" && c <="9")   

            {   

            if (digit_cnt == undefined)        

                digit_cnt = 1;   

            else                           

                ++digit_cnt;   

   

            s1 += c;   

            }   

        else if (digit_cnt != undefined)    

            {   

            if (digit_cnt < max_digit) for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";   

   

            s1 += c;   

            digit_cnt = undefined;   

            }   

        else   

            {   

            s1 += c;   

            }   

        }   

   

    if (digit_cnt != undefined && digit_cnt < max_digit)   

        for (var n = 0; n < max_digit-digit_cnt; n++) s1 += "0";   

   

    len = s1.length;   

   

    var ret = "";   

   

    for (var i = len-1; i >= 0; i--) ret += s1.charAt(i);   

   

    ret = ret.toUpperCase();   

   

    return ret;   

    }   

Likes

translate

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
Adobe Community Professional ,
Jul 26, 2018 Jul 26, 2018

Copy link to clipboard

Copied

btw now your script is twice faster than mine. Both are exremaly fast though, but yours does not regroup layers - it s why

Likes

translate

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
Adobe Community Professional ,
Feb 26, 2018 Feb 26, 2018

Copy link to clipboard

Copied

Before you run script go to 'Layers' panel, select 'Panel Oprions' from dropdown menu (you open clicking icon in right upper corner of panel), make 'Add "copy" to copied Layers and Groups' box empty. Script will be working if that won't be done, but all layer(Set)s get 'copy' word in their names. It is possible to remove it by script, but I did not do it as because of Photoshop bug there is need to select each single layer before renaming what increases time of performance. If you want script worked extremaly fast, press 'tab' key to toggle off your panels (it's scriptable but I let user decide of it). Script works for background and all kind of layers (those nasted in all levels groups as well). If any group will be selected before running script it is going to be unselected. Sorting is based on modified order of ASCII characters, so generally it is the same for digits and non letter characters. Difference is that order for letters is like: AaBbCc(...)Zz. Script distinguishes letters case and numbers in names - not as single digits but row of them - so there will be for ex. this order applied: 0, 1, 2, 3, 10, 99, 100 and so on. I didn't sort for any possible case there can happen but probably noone uses so complex layers names it ever was needed to cover unusual namings. It works also for layers having same content and length, while for example 'A' named layer will be sorted before 'a' named layer. I did not play with (disabled) linked layers, however those selected keep their links to each other while lose to those they were unselected and disabled ones gets enabled. These properties are possible to retrieve but as far there is no need to do it I didn't spend time for it. Visibility, effects and transparency stay untouched. It took 7,5 second with toggled off panels for 299 layer with background, so it gives 1 second for 40 layers! If you get some errors let me know so I'll make fixes.

gT = new Date().getTime(), $.level = 0; function sTT(v) {return stringIDToTypeID(v)}

function arr(v) {

     (ref1 = new ActionReference()).putProperty(sTT('property'), tL = sTT('targetLayers'))

     ref1.putEnumerated(sTT('document'), sTT('ordinal'), sTT('targetEnum'))

     if ((dsc1 = executeActionGet(ref1)).hasKey(tL)) {for(lst = dsc1.getList(tL), i = 0; i < lst.count;) {

          (ref2 = new ActionReference()).putIndex(sTT('layer'), lst.getReference(i++).getIndex() + bG), s = 'Start'

          if (typeIDToStringID((dsc = executeActionGet(ref2)).getEnumerationValue(sTT(lS = 'layerSection'))) == lS + s)

               {ref2.putIdentifier(sTT('layer'), dsc.getInteger(sTT('layerID'))), dsc1.putReference(sTT('null'), ref2)

               dsc1.putEnumerated(sTT(sM = 'selectionModifier'), sTT(sM + 'Type'), sTT('removeFromSelection'))

               dsc1.putBoolean(sTT('makeVisible'), false), executeAction(sTT('select'), dsc1, DialogModes.NO)}

          else v.unshift([dsc.getString(sTT('name')), dsc.getInteger(sTT('layerID'))])}

     }

     return v

}

(ref = new ActionReference()).putIdentifier(sTT('layer'), 1)

try{bG = !(dsc = executeActionGet(ref)).getBoolean

(sTT('background'))} catch (err) {bG = 1}

if ((sL = arr([])).length > 1) {

     (srt = Array().slice.call(sL)).sort(function(v1, v2) {

          function l(v) {return v.length}; if ((v1 = v1[0]) != v2 = v2[0]) {

               for(g = 0; g < l(v1); g++) {

                    if ((v1g = v1) != v2g = v2) {

                         function dig(v) {return !isNaN(v)}

                         if (dig(v1g) && dig(v2g)) {

                              function slc(v) {

                                   return +String(v).slice(g).match(/\d+/)

                              }

                              return slc(v1) - slc(v2)

                         }

                         function cCA(v) {

                              return v.charCodeAt() -

                              (/[a-z]/.test(v) ? 31.5 : 0)

                         }

                         if (v1g && v2g) return cCA(v1g) - cCA(v2g)

                    }

                    if (!v1g || !v2g) return l(v1) - l(v2)

               }

          }

     });

     (ref1 = new ActionReference()).putEnumerated(sTT('layer'), sTT('ordinal'),

     sTT('targetEnum')); (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1)

     dsc1.putInteger(sTT('version'), 5), executeAction(sTT('duplicate'), dsc1, DialogModes.NO);

     dL = arr([]);

     (ref1 = new ActionReference()).putClass(sTT('layerSection'));

     (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);

     (ref2 = new ActionReference()).putEnumerated(sTT('layer'),

     sTT('ordinal'), sTT('targetEnum')), dsc1.putReference(sTT

     ('from'), ref2), executeAction(sTT('make'), dsc1, DialogModes.NO)

     grp = executeActionGet(ref2).getInteger(sTT('layerID' ))

     function del(v1, v2) {

          eval("(ref1 = new ActionReference()).put" + v1 + "(sTT('layer'), v2)");

          (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1)

          executeAction(sTT('delete'), dsc1, DialogModes.NO)

     }

     for(i = 0; i < sL.length; i++) {

          for(j = 0; j < srt.length; j++) {

               if (sL[1] == srt[1]) {

                    (ref1 = new ActionReference()).putIdentifier(sTT('layer'), dL.shift()[1]);

                    (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);

                    (ref1 = new ActionReference()).putIdentifier(sTT('layer'), sL[1])

                    t = executeActionGet(ref1).getInteger(sTT('itemIndex'));

                    (ref2 = new ActionReference()).putIndex(sTT('layer'), t)

                    dsc1.putReference(sTT('to'), ref2), executeAction

                    (sTT('move'), dsc1, DialogModes.NO);

                    del('Index', t - (bG ? 0 : 1))

                    if (!bG && t == 1) {

                         (ref1 = new ActionReference()).putClass(sTT('backgroundLayer'));

                         (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);

                         (ref2 = new ActionReference()).putIndex(sTT('layer'), t);

                         dsc1.putReference(sTT('using'), ref2), executeAction

                         (sTT('make'), dsc1, DialogModes.NO)

                    }

                    break

               }

          }

     }

     del('Identifier', grp), new Date().getTime() - gT

}

Likes

translate

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 ,
Feb 09, 2021 Feb 09, 2021

Copy link to clipboard

Copied

This dosnt work for me.. i simple her an error: 

https://www.dropbox.com/s/2rhkl0hmlo8wdpm/Screenshot%202021-02-09%20at%2009.56.23.png?dl=0

- Numerical value expected.

Line:75 

it seems to be erroring in the try-catch phase? Note that my document does not have a background layer.

Likes

translate

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
Adobe Community Professional ,
Feb 09, 2021 Feb 09, 2021

Copy link to clipboard

Copied

You mean the code of r-bin (that contains try..catch statement) or mine you updated of?

Likes

translate

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 ,
Feb 10, 2021 Feb 10, 2021

Copy link to clipboard

Copied

Yes sorry, the answer posted by r-bin above was throwing the error.  I found an alternative script on Github that worked instead in the end. 

 

var layers = activeDocument.layers;
    var layersArray = [];
    var len = layers.length;
    


    // store all layers in an array
    for (var i = 0; i < len; i++) {
        layersArray.push(layers[i]);
    }

    // sort layer top to bottom
    layersArray.sort();

    for (i = 0; i < len; i++) {
        layersArray[i].move(layers[i], ElementPlacement.PLACEBEFORE);
    }

 

Likes

translate

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