1
votes

Background: I'm writing a script to clean up all non-visible layers in a photoshop document. Photoshop layers can be grouped and groups can be nested similar to a directory tree so I'm using recursion.

The problem I'm having is there is there is no foreach function in JavaScript to iterate through each layer in a set. I used for(i=0; i<layers.length; i++) etc but each time I remove a layer it changes the index of the layers and eventually fails. I fixed this by pushing each layer to a new array and removing them with another for loop. I'm having the same problem with removing empty groups.

Anyway, I have a *semi-working function but it seems like a mess. I tried using for-in loop but it loops through properties like .length and .parent instead of the actual layers. Is there a foreach alternative that would work in this situation? Do I need to run two separate recursive functions to remove the layers then to remove empty groups? Thanks!

Here is the semi-working script.

And a sample .psd document.

If you're unfamiliar with running scripts in photoshop just select File>Scripts>Browse and select the .jsx file.

1
It does, and the documentation really sucks.Diodeus - James MacFarlane

1 Answers

6
votes

There are a number of possible solutions here:

Backwards Iteration

One typical way to solve an issue like this is to iterate the array from the end to the front. That way, when you remove the item you're currently interating, it doesn't affect the array position of the elements you still need to iterate:

for (i=layers.length - 1; i >= 0; i--)

Make a Static Copy of the Array

Another possibility is to make a copy of the array so you have a static copy to iterate through that isn't being modified when layers are removed:

var staticLayersArray = Array.prototype.slice.call(layers);
for (i=0; i < staticLayersArray.length; i++) {
    // process staticLayersArray[i]
}

Accumulate Items to Delete

You can iterate through the array and accumulate the items to delete and only delete them when you're done with the iteration:

var layersToDelete = [];
for (i=0; i < layers.length; i++) {
    if (we need to delete this item) {
        layersToDelete.push(layers[i]);
    }
}
// after the iteration, process the deletions
for (i = 0; i < layersToDelete.length; i++) {
    // remove the layer layersToDelete[i];
}

FYI, modern implementations of javascript arrays do have a .forEach() method, but it is not safe from changing the array while iterating so you would still have to use some other solution anyway.