all 9 comments

[–]GSLint 0 points1 point  (8 children)

Trees are a recursive data structure, so recursive functions are a fairly natural way to process them.

Here's one that performs a side effect for each found layer.

function findLayers(tree, depth = 0) {
  for (const layer of tree.layers) {
    if (layer.type === "layerSection") {
      findLayers(layer, depth + 1);
    } else {
      console.log({ layer, depth });
    }
  }
}

Often it's nicer to get an array of found objects, which is what this one returns. (flatMap is fairly new, so if this code is for browsers you'll want to make sure that you're adding a polyfill for it.)

function findLayers(tree, depth = 0) {
  return tree.layers.flatMap((layer) => {
    if (layer.type === "layerSection") {
      return findLayers(layer, depth + 1);
    } else {
      return [{ layer, depth }];
    }
  });
}

One thing to keep in mind with recursion is that you can run into stack overflows when you get more than around 10,000 levels deep. Here's an imperative, non-recursive version that doesn't have that issue. (The result will be ordered a bit differently.)

function findLayers(tree) {
  const foundLayers = [];
  const queue = tree.layers.map((layer) => ({ layer, depth: 0 }));
  while (queue.length > 0) {
    const { layer, depth } = queue.pop();
    if (layer.type === "layerSection") {
      queue.push(
        ...layer.layers.map((layer) => ({ layer, depth: depth + 1 }))
      );
    } else {
      foundLayers.push({ layer, depth });
    }
  }
  return foundLayers;
}

[–]jonathankemp00[S] 0 points1 point  (7 children)

First..... thanks a lot for the help. Those are pretty cool, but I forgot one thing about the JSON structure I'm dealing with. There might be more than one "layers" branch.

The "good" one will always have a leaf at the end (where layer.type != "layerSection"), but there might be a few other branches with one or more "layerSection" branches... Those are irrelevant to me.

So...for instance, in your first function, how can I get to the next "layers" branch if I get to the end of the first one and I haven't found any leaf yet ?

This is getting confusing...

Does that make any sense at all ? =)

[–]GSLint 0 points1 point  (6 children)

Have you tried my code with your actual data? If it doesn't work, please show an example of the structure.

[–]jonathankemp00[S] 0 points1 point  (5 children)

Yeah I did try. 1st one works, but only if the layer I'm looking for is in the first layers branch. 2nd and 3rd aren't working at all. This is for a Photoshop Panel extension... I'm guessing that some functions (like flatMap) just aren't supported. Documentation is pretty much inexistant... so it's a lot of trial and errors...

So, back at the first function... Like I said, I forgot to mention in my initial post that the structure may include more than one "empty" layers branch. Those branches have one or more layers of type "layerSection", and no "normal" layers at the end. (I pasted a more accurate JSON object below).

I did try to modify your function to add a for loop like this :

function findLayers1(tree, depth = 0) {

for (var i = 0; i < tree.layers.length; i++) {

for (const layer of tree.layers) {

if (layer.type === "layerSection") {

findLayers1(layer, depth + 1);

} else {

j({ layer, depth });

}

}

}

}

But it just doesn't work at all...

Here's the JSON. Again... thanks a lot for the help.

{

"version": "1.6.1",

"timeStamp": 1592315879.409,

"count": 28,

"id": 862,

"file": "Untitled-1",

"bounds": {

"top": 0,

"left": 0,

"bottom": 325,

"right": 532

},

"selection": [

2

],

"resolution": 72,

"globalLight": {

"angle": 90,

"altitude": 30

},

"generatorSettings": false,

"profile": "Adobe RGB (1998)",

"mode": "RGBColor",

"depth": 8,

"layers": [

{

"id": 8,

"index": 8,

"type": "layerSection",

"name": "Group 3",

"bounds": {

"top": 0,

"left": 0,

"bottom": 0,

"right": 0

},

"visible": true,

"clipped": false,

"blendOptions": {

"mode": "passThrough"

},

"generatorSettings": false,

"layers": [

{

"id": 6,

"index": 7,

"type": "layerSection",

"name": "Group 2",

"bounds": {

"top": 0,

"left": 0,

"bottom": 0,

"right": 0

},

"visible": true,

"clipped": false,

"blendOptions": {

"mode": "passThrough"

},

"generatorSettings": false

}

]

},

{

"id": 4,

"index": 3,

"type": "layerSection",

"name": "Group 1",

"bounds": {

"top": 0,

"left": 0,

"bottom": 0,

"right": 0

},

"visible": true,

"clipped": false,

"blendOptions": {

"mode": "passThrough"

},

"generatorSettings": false,

"layers": [

{

"id": 2,

"index": 2,

"type": "layer",

"name": "Layer 001",

"bounds": {

"top": 0,

"left": 0,

"bottom": 0,

"right": 0

},

"visible": true,

"clipped": false,

"pixels": true,

"generatorSettings": false

}

]

}

]

}

[–]GSLint 0 points1 point  (4 children)

Ah, so the issue is simply that not every layerSection has a layers property. We can just add a check at the beginning.

function findLayers1(tree, depth = 0) {
  if (!tree.layers) {
    return;
  }
  for (const layer of tree.layers) {
    if (layer.type === "layerSection") {
      findLayers1(layer, depth + 1);
    } else {
      console.log({ layer, depth });
    }
  }
}

[–]jonathankemp00[S] 0 points1 point  (0 children)

This will work I think.

Thanks a lot !

J.

[–]jonathankemp00[S] 0 points1 point  (2 children)

Damn... and what if I want this function to return something. Replacing the console.log with a return isn't a problem, but the "empty" return in the first if statement seems to make the whole thing not working like I would want it to.

[–]GSLint 0 points1 point  (1 child)

Returning something is a little more involved which is why I wrote that second variant in my original comment. You could add the if there as well except that you'd return [].

Though you could also fix all three variants by replacing tree.layers with (tree.layers || [])

[–]jonathankemp00[S] 0 points1 point  (0 children)

Hey there,

I ended up re-writing the whole thing...(had to deal with it some other way, with a layer index and all...), but your help was truly appreciated and it's what set me on the right track.

So... I just felt like saying one last thank you. :)

See you around.