1

I have a graph of tag names like coffee, shoes etc. Each tag can have any multiple parent or child. I am making a tree of out that. I picked those nodes which don't have parent and start traversing from that. My mongo id

  Taxonomy.find($or: [{ 'parent': { $exists: false } }]}).then((resp) => {
    Promise.all(resp.map(resp) => getChildCategories(resp.children)).then(function(results) {
        resp.children = results
        res.json({resp});
    }).catch(err => {
        console.log(err)
    });
});

But I stuck when there is a circular condition like a tag has a child and that child has the same parent so it got into circular condition. I am using es5 so no async await.

var visited_nodes = {"5a8c1c966ac6cb3c078fe727" : true};

//this map keep track of visited nodes

function getChildCategories(parentCategory){
return parentCategory.map(child => {
  return new Promise((resolve,reject) => {
  if(!visited_nodes[child]){
    Taxonomy.findOne({_id : child}).then((resp) => {
       visited_nodes[child] = true;
       console.log(resp.children);
       if(resp.children && resp.children.length > 0){
           getChildCategories(resp.children)
           .map(x => x).then(childresp => {
               resp.children = childresp;
               resolve([resp]);
           })
       }else{
         resp.children = null;
         resolve(resp);
       }
    }).catch(err => {
        reject(err);
    });
   }else{
       console.log("already visited")
       return resolve({});
   }
  });
});
};

Due to asynchronous DB call, it is difficult to make a tree because map function returns null in the async call. Anyone has a solution about how to perform this

sumit
  • 37
  • 3
  • The for...of or for...in loop make sync call you can also set your map function async `yourItems.map( async item => {....} ) – Maxime Girou Mar 15 '19 at 14:13
  • @MaximeGirou I am not using async await my node version is 6 can you write this in es5? if yes please share your code – sumit Mar 15 '19 at 14:16
  • What do you mean by "*map function returns null in the async call*"? – Bergi Mar 15 '19 at 14:19
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Mar 15 '19 at 14:19
  • @Bergi Yes I don't know how to write this in javascript due to asynchronous call – sumit Mar 15 '19 at 14:20
  • `getChildCategories` should return a promise, not an array of promises. Just like in your first snippet, use `Promise.all`! – Bergi Mar 15 '19 at 14:20
  • @Bergi can you edit my code and share ? – sumit Mar 15 '19 at 14:22

1 Answers1

0

You seem to be looking for

function getCategory(category) {
  if (visited_nodes[category]) {
    console.log("already visited")
    return Promise.resolve({});
  }
  visited_nodes[category] = true;
  return Taxonomy.findOne({_id : category}).then(resp => {
    return getChildCategories(resp.children).then(children =>
      resp.children = children;
      return resp;
    });
  });
}
function getChildCategories(children) {
  console.log(children);
  if (children && children.length > 0) {
    return Promise.all(children.map(getCategory));
  } else {
    return Promise.resolve(null);
  }
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Nice but how its working , in case when we already visited it resolves an empty object and i see only ids in child array , dont it be an object ? – sumit Mar 15 '19 at 19:23
  • I took the empty object in the already-visited case from your code. Yes, the `children` argument of `getChildCategories` should be an array of ids, not an object. Why did you expect one? – Bergi Mar 16 '19 at 09:38