Question on looping through array with sub-arrays

Started by axel.hennig, March 18, 2022, 11:42:22 PM

Previous topic - Next topic

axel.hennig

Hello everybody,

can someone give me some hint on how to loop through the response of the following call:


IMWS.get('v1/categories',{
    id: 123,
    recursive: true,
    fields: 'children,childrenids,name,level',
}).then(response => {
    console.log(response);
});


If my category-tree looks like this

Level 0
    Level 1 a
        Level 2 a
        Level 2 b
            Level 3 a
            Level 3 b
        Level 2 c
    Level 1 b
        Level 2 d
        Level 2 e


I would like to loop through it from top to bottom (0 -> 1a -> 2a -> 2b -> 3a -> 3b -> 2c -> 1b -> 2d -> 2e).

Is this somehow possible?

Mario

#1
You'll need a recursive function for that.

In the caller you iterate over the array of categories.

For each category you call a function that takes the category and does something.
This function then iterates over the children (if any) of the category and calls "itself".

For example:

The caller (the function that calls the IMWS endpoint). Note that I use async/await here to make things easy:

async function SomeFunction()
{
  let result = await IMWS.get('v1/categories',{
      id: 'root',
      recursive: true,
      fields: 'children,childrenids,name,level',
  });

  result.categories.forEach(c => {
      processCategory(c);
  });
}


This function does the work. It gets a category as parameter and does whatever is needed.
It then iterates over the children of the category and calls itself, recursively.
This way you go down the hierarchy. It outputs an indented list of categories to the console.

function processCategory(c)
{
    let s = '';
    for (let i = 0; i < c.level; i++)
    {
        s += ' ';
    }
    console.log(`${s}${c.name} ${c.level}`);

    if (c.children) {
        c.children.forEach(c => {
            processCategory(c);
        });
    }
}


Again, in color:
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

axel.hennig

Hi Mario,

thanks a lot, your support is really:

  • great
  • fast
  • helpful

It took me a while to understand how this recursive function is working, but I think I've understood it.

Again: Thanks a lot.

Mario

QuoteIt took me a while to understand how this recursive function is working, but I think I've understood it.

That's perfectly normal.
Every programming rookie needs to wrap his head around recursion. Just one of the many powerful tools in your toolbox.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

axel.hennig

I would have the next question on that topic.

I've slightly changed the above (first) mentioned function to:


function SomeFunction () {
    IMWS.get('v1/categories',{
        id: 'root',
        recursive: true,
        fields: 'children,name',
    }).then(response => {
        response.categories.forEach(c => {
            processCategory(c);
        });
    });
}


The second part is:


function processCategory (c) {
   
    console.log(c.name);
    if (c.children) {
        c.children.forEach(c => {
            processCategory(c);
        });
    }

}


This works perfectly fine, but now I wanted to save the console.log to a textfile. Therefore I changed the second function to:


function processCategory (c) {
   
    IMatch.writeTextFile({
        filename: 'D:\\IMatch_tmp\\test.txt',
        data: c.name + '\r\n',
        append: true,
    }).then(response => {
        console.log(c.name);
        if (c.children) {
            c.children.forEach(c => {
                processCategory(c);
            });
        }
    });

}


This one does not work anymore. Main issue is that the order of the categories is mixed up (both, on the console as well as in the txt-file). I thought I did it correct with using jquery-then, but aparently it does not seem to work.

Does someone know why and can help me here?

Mario

#5
Keep in mind that things like  IMatch.writeTextFile are asynchronous.
When you call it, JavaScript does not wait but immediately continues with the next line.
Which then causes another writeTextFile call, while the previous ones are probably still processing.  ;D

Basically, your function calls, in a very tight loop, writeTextFile dozens, maybe hundreds of times. Within a few seconds! Without waiting.
The order in which asynchronous operations are processed is not determined, there is caching involved, the IMWS thread pool may run empty, file system caching etc.

You could use await IMWS.writeTextFile... to make this synchronous. But that would be bad for performance.

Much easier would be to use a text variable in memory to keep what you want to write, and then call writeTextFile once when complete.
Use this code as an example:



It uses a string as a buffer.
The process category now returns a string, which is concatenated with the current value in textData.

The reason why I used a function result and not a string parameter is because strings are immutable in JavaScript, which would make using a parameter more tricky.
The solution shown above is neat and applicable to many other situations.

When Processcategory finally returns to SomeFunction, textData contains the list of categories.
Then SomeFunction calls writeTextFile once to save it.

-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

axel.hennig

And again, thanks Mario. Works perfectly.

My hope is that right now I will be able to finish my little project without further questions, but my guess is that I will ask a lot more questions in this forum...

Mario

#7
Asynchronous programming has flabbergasted many.
But it is a powerful tool to "keep the UI responsive" while your app does things in the background. And to utilize all the power of the no common multiple processors.
If I ever shall write a new IMatch, I will utilize this to the max - instead of refactoring parts of IMatch one after each other to utilize this.

Thanks to async/await in JavaScript, things have become much easier.
Allowing you to think linearly while your program works asynchronously.
It'll take a couple of hours or days to wrap your head around it. Normal.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook