Home > Enterprise >  Node - build a tree recursively with API data
Node - build a tree recursively with API data

Time:08-12

I need to build a tree like structure using data from an API.

The structure i start with is as follows:

{
   "type": "group",
   "id": 1,
   "name": "rootGroup",
   "members": [],
}

There will always be a root group as the base of the tree.

I have a function named getMembersInGroup(groupId) which is an API call and returns something like:

[
  {
   "type": "group",
   "id": 77,
   "name": "IT group",
  },
  {
    "type": "user",
    "id": 40,
    "name": "John"
  }
]

Members can either be of type user or another group. So a user would look like:

{
  "type": "user",
  "id": 40,
  "name": "John"
}

If it's another group it needs to recursively fetch those until there are only users or empty array left in members.

Any group can have users at any level with the tree.

A mock of getMembersInGroup:

const getMembersInGroup = async (groupId) => {
  try {
    const members = await fetch.callApi('/groups/'   groupId   '/members');

    if (members) {  
      return members;
    }
    else {
      return [];
    }
  } catch (error) {
    return { error };
  }
}

The end result should look like this:

{
   "type": "group",
   "id": 1,
   "name": "rootGroup",
   "members": [
     {
       "type": "group",
       "id": 88,
       "name": "Some group",
       "members": [
         {
           "type": "user",
           "id": 231,
           "name": "SALLY"
         },
         {
           "type": "user",
           "id": 232,
           "name": "Henry"
         }
       ]
     },
     {
       "type": "user",
       "id": 41,
       "name": "Chris"
     }
   ],
}

I need help with the algorithm to create the tree.

CodePudding user response:

Your getMembersInGroup function could look like this:

const getMembersInGroup = async (groupId) => {
    const members = (await fetch.callApi(`/groups/${groupId}/members`)) ?? [];

    for (const member of members) {
        if (member.type == "group") {
            member.members = await getMembersInGroup(member.id);
        }
    }
    return members;
}

Call it like this:

async function loadTree() {
    return {
        type: "group",
        id: 1,
        name: "rootGroup",
        members: await getMembersInGroup(1)
    };
}

loadTree().then(result =>
    console.log(result);
    // Work with the result ...
).catch(error =>
    console.log("error: ", error)
);

Demo with a mock implementation of fetch.callApi:

// Mock for fetch.callApi 
const fetch = {
    mockData: [0,[2,3,4],[5,6,7],[8,9],0,0,0,[10],0,0,[11,12],0,0],
    callApi(url) {
        return new Promise((resolve, reject) => {
            const groupId =  url.split("/")[2];
            const children = this.mockData[groupId];
            if (!children) return reject("not found: "   groupId);
            const result = children.map(id => {
                const type = this.mockData[id] ? "group" : "user";
                return {type, id, name: type   "_"   id};
            });
            setTimeout(() => resolve(result), 50);
        });
    }
}

async function loadTree() {
    return {
        type: "group",
        id: 1,
        name: "rootGroup",
        members: await getMembersInGroup(1)
    };
}

const getMembersInGroup = async (groupId) => {
    const members = (await fetch.callApi('/groups/'   groupId   '/members')) ?? [];

    for (const member of members) {
        if (member.type == "group") {
            member.members = await getMembersInGroup(member.id);
        }
    }
    return members;
}

loadTree().then(result =>
    console.log(JSON.stringify(result, null, 2))
).catch(error =>
    console.log("error: ", error)
);

CodePudding user response:

You can do something like:

 const getMembersInGroup = async (groupId) => {
  try {
    const members = await fetch.callApi('/groups/'   groupId   '/members');

    if (members) {  
      foreach(member in members) {
        if (member.type == 'groups') {
          member.members = getMembersInGroup(member.groupid)
        }
      }
      return members;
    }
    else {
      return [];
    }
  } catch (error) {
    return { error };
  }
}

So you have the recursion only if it's a group type, otherwise the member is returned as is.

  • Related