Author Topic: Question on looping through array with sub-arrays  (Read 378 times)

axel.hennig

  • Full Member
  • **
  • Posts: 219
Question on looping through array with sub-arrays
« on: March 18, 2022, 11:42:22 PM »
Hello everybody,

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

Code: [Select]
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
Code: [Select]
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

  • IMatch Developer
  • Administrator
  • *****
  • Posts: 31554
Re: Question on looping through array with sub-arrays
« Reply #1 on: March 19, 2022, 08:14:25 AM »
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:

Code: [Select]
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.

Code: [Select]
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:
« Last Edit: March 19, 2022, 08:16:37 AM by Mario »

axel.hennig

  • Full Member
  • **
  • Posts: 219
Re: Question on looping through array with sub-arrays
« Reply #2 on: March 20, 2022, 06:57:32 PM »
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

  • IMatch Developer
  • Administrator
  • *****
  • Posts: 31554
Re: Question on looping through array with sub-arrays
« Reply #3 on: March 20, 2022, 07:21:22 PM »
Quote
It 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.

axel.hennig

  • Full Member
  • **
  • Posts: 219
Re: Question on looping through array with sub-arrays
« Reply #4 on: March 22, 2022, 10:39:05 PM »
I would have the next question on that topic.

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

Code: [Select]
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:

Code: [Select]
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:

Code: [Select]
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

  • IMatch Developer
  • Administrator
  • *****
  • Posts: 31554
Re: Question on looping through array with sub-arrays
« Reply #5 on: March 23, 2022, 09:33:49 AM »
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.

« Last Edit: March 23, 2022, 09:36:19 AM by Mario »

axel.hennig

  • Full Member
  • **
  • Posts: 219
Re: Question on looping through array with sub-arrays
« Reply #6 on: March 23, 2022, 09:44:59 PM »
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

  • IMatch Developer
  • Administrator
  • *****
  • Posts: 31554
Re: Question on looping through array with sub-arrays
« Reply #7 on: March 23, 2022, 10:10:56 PM »
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.
« Last Edit: March 23, 2022, 10:13:48 PM by Mario »