I am trying to create a hierarchical structure so that I can pass the data to a plugin I am using to generate an Org Chart.
For my org chart, I have all the data I need to work with such as the employees and some basic data about them.
FirstName
LastName
EmployeeID
ManagerEmployeeID
Manager Name
This data is stored in an array with no nesting or linking between the different levels, which is what I am trying to achieve with some jquery/javascript.
My goal is to take this array and nest it based on the ManagerID and EmployeeID so I can make a tree hierarchy.
I am unable to change how the data is provided to me or else I would have nested it from the database response.
Example Data:
• Tom Jones
o Alice Wong
o Tommy J.
• Billy Bob
o Rik A.
♣ Bob Small
♣ Small Jones
o Eric C.
My flat data example:
{
"FirstName": "Tom"
"LastName": "Jones"
"EmployeeID": "123"
"ManagerEmployeeID": ""
"Manager Name": ""
},
{
"FirstName": "Alice"
"LastName": "Wong"
"EmployeeID": "456"
"ManagerEmployeeID": "123"
"Manager Name": "Tom Jones"
},
{
"FirstName": "Tommy"
"LastName": "J."
"EmployeeID": "654"
"ManagerEmployeeID": "123"
"Manager Name": "Tom Jones"
},
{
"FirstName": "Billy"
"LastName": "Bob"
"EmployeeID": "777"
"ManagerEmployeeID": ""
"Manager Name": ""
},
{
"FirstName": "Rik"
"LastName": "A."
"EmployeeID": "622"
"ManagerEmployeeID": "777"
"Manager Name": "Billy Bob"
},
{
"FirstName": "Bob"
"LastName": "Small"
"EmployeeID": "111"
"ManagerEmployeeID": "622"
"Manager Name": "Rik A."
},
{
"FirstName": "Small"
"LastName": "Jones"
"EmployeeID": "098"
"ManagerEmployeeID": "622"
"Manager Name": "Rik A"
},
{
"FirstName": "Eric"
"LastName": "C."
"EmployeeID": "222"
"ManagerEmployeeID": "777"
"Manager Name": "Billy Bob"
}
Example Desired Output:
{
"FirstName": "Tom",
"LastName": "Jones",
"EmployeeID": "123",
"ManagerEmployeeID": "",
"Manager Name": "",
{
"FirstName": "Alice",
"LastName": "Wong",
"EmployeeID": "456",
"ManagerEmployeeID": "123",
"Manager Name": "Tom Jones",
},
{
"FirstName": "Tommy",
"LastName": "J.",
"EmployeeID": "654",
"ManagerEmployeeID": "123",
"Manager Name": "Tom Jones",
},
},
{
"FirstName": "Billy",
"LastName": "Bob",
"EmployeeID": "777",
"ManagerEmployeeID": "",
"Manager Name": "",
{
"FirstName": "Rik",
"LastName": "A.",
"EmployeeID": "622",
"ManagerEmployeeID": "777",
"Manager Name": "Billy Bob",
,
{
"FirstName": "Bob",
"LastName": "Small",
"EmployeeID": "111",
"ManagerEmployeeID": "622",
"Manager Name": "Rik A.",
},
{
"FirstName": "Small",
"LastName": "Jones",
"EmployeeID": "098",
"ManagerEmployeeID": "622",
"Manager Name": "Rik A",
},
},
},
{
"FirstName": "Eric",
"LastName": "C.",
"EmployeeID": "222",
"ManagerEmployeeID": "777",
"Manager Name": "Billy Bob",
}
Are there any best practices to acomplish something like this? If I had the ability to do this on the database level, I would. However, I only have a list of the data along with the corrolation between the manager and employee based on the IDs.
4 Answers 4
You can create recursive function with reduce() and that will return desired data structure.
var data = [{"FirstName":"Tom","LastName":"Jones","EmployeeID":"123","ManagerEmployeeID":"","Manager Name":""},{"FirstName":"Alice","LastName":"Wong","EmployeeID":"456","ManagerEmployeeID":"123","Manager Name":"Tom Jones"},{"FirstName":"Tommy","LastName":"J.","EmployeeID":"654","ManagerEmployeeID":"123","Manager Name":"Tom Jones"},{"FirstName":"Billy","LastName":"Bob","EmployeeID":"777","ManagerEmployeeID":"","Manager Name":""},{"FirstName":"Rik","LastName":"A.","EmployeeID":"622","ManagerEmployeeID":"777","Manager Name":"Billy Bob"},{"FirstName":"Bob","LastName":"Small","EmployeeID":"111","ManagerEmployeeID":"622","Manager Name":"Rik A."},{"FirstName":"Small","LastName":"Jones","EmployeeID":"098","ManagerEmployeeID":"622","Manager Name":"Rik A"},{"FirstName":"Eric","LastName":"C.","EmployeeID":"222","ManagerEmployeeID":"777","Manager Name":"Billy Bob"}];
function makeTree(data, parentId) {
return data.reduce(function(r, e) {
if (e.ManagerEmployeeID == parentId) {
var employees = makeTree(data, e.EmployeeID);
if (employees.length) e.employees = employees
r.push(e)
}
return r;
}, [])
}
console.log(makeTree(data, ''))
9 Comments
data array?Desired output is not valid data structure for javascript. You need some key in that object to point to "children". To the solution - usualy you traverse through whole object saving ID as key in object with the rest as the value for given id. For example
let tree = {}
flat.forEach(function(item) {
item.children = [];
tree[item.id] = item // build whole tree
// in case it is ordered by parent, you can use
tree[item.parentId].children.push(item);
})
// in case it is not ordered, you have to traverse the structure again
flat.forEach(function(item) {
tree[item.parentId].children.push(item);
})
Comments
You could use a single loop approach and check the parents, aka ManagerEmployeeID and the children.
This proposal works for unsorted data as well, without recursion.
var data = [{ FirstName: "Tom", LastName: "Jones", EmployeeID: "123", ManagerEmployeeID: "", "Manager Name": "" }, { FirstName: "Alice", LastName: "Wong", EmployeeID: "456", ManagerEmployeeID: "123", "Manager Name": "Tom Jones" }, { FirstName: "Tommy", LastName: "J.", EmployeeID: "654", ManagerEmployeeID: "123", "Manager Name": "Tom Jones" }, { FirstName: "Billy", LastName: "Bob", EmployeeID: "777", ManagerEmployeeID: "", "Manager Name": "" }, { FirstName: "Rik", LastName: "A.", EmployeeID: "622", ManagerEmployeeID: "777", "Manager Name": "Billy Bob" }, { FirstName: "Bob", LastName: "Small", EmployeeID: "111", ManagerEmployeeID: "622", "Manager Name": "Rik A." }, { FirstName: "Small", LastName: "Jones", EmployeeID: "098", ManagerEmployeeID: "622", "Manager Name": "Rik A" }, { FirstName: "Eric", LastName: "C.", EmployeeID: "222", ManagerEmployeeID: "777", "Manager Name": "Billy Bob" }],
tree = function (data, root) {
var r = [],
o = {};
data.forEach(function (a) {
a.children = o[a.EmployeeID] && o[a.EmployeeID].children;
o[a.EmployeeID] = a;
if (a.ManagerEmployeeID === root) {
r.push(a);
} else {
o[a.ManagerEmployeeID] = o[a.ManagerEmployeeID] || {};
o[a.ManagerEmployeeID].children = o[a.ManagerEmployeeID].children || [];
o[a.ManagerEmployeeID].children.push(a);
}
});
return r;
}(data, '');
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The problem was, you need to specify the root, in this case MgrQID: "5555", where "5555" is the root value for calling the function
tree = function (data, root) {
// code
}(data, "5555");
// ^^^^^^
var data = [{ QID: "1234", MgrQID: "5555", PositionTitle: "Manager, Systems Administration 3" }, { QID:"5678", MgrQID: "1234", PositionTitle: "Systems Administrator 3" }],
tree = function (data, root) {
var r = [],
o = {};
data.forEach(function (a) {
a.children = o[a.QID] && o[a.QID].children;
o[a.QID] = a;
if (a.MgrQID === root) {
r.push(a);
} else {
o[a.MgrQID] = o[a.MgrQID] || {};
o[a.MgrQID].children = o[a.MgrQID].children || [];
o[a.MgrQID].children.push(a);
}
});
return r;
}(data, "5555");
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
1 Comment
You can use breadth-first search to find the parent employee in the tree and do the insertion to its employees.
Try like below.
const data = [{"FirstName":"Tom","LastName":"Jones","EmployeeID":"123","ManagerEmployeeID":"","Manager Name":""},{"FirstName":"Alice","LastName":"Wong","EmployeeID":"456","ManagerEmployeeID":"123","Manager Name":"Tom Jones"},{"FirstName":"Tommy","LastName":"J.","EmployeeID":"654","ManagerEmployeeID":"123","Manager Name":"Tom Jones"},{"FirstName":"Billy","LastName":"Bob","EmployeeID":"777","ManagerEmployeeID":"","Manager Name":""},{"FirstName":"Rik","LastName":"A.","EmployeeID":"622","ManagerEmployeeID":"777","Manager Name":"Billy Bob"},{"FirstName":"Bob","LastName":"Small","EmployeeID":"111","ManagerEmployeeID":"622","Manager Name":"Rik A."},{"FirstName":"Small","LastName":"Jones","EmployeeID":"098","ManagerEmployeeID":"622","Manager Name":"Rik A"},{"FirstName":"Eric","LastName":"C.","EmployeeID":"222","ManagerEmployeeID":"777","Manager Name":"Billy Bob"}];
const nestedObj = {
employees: [],
};
// if found the parent then append it and return true, otherwise return false
const findParentAndAppendChild = (ID, obj, employee) => {
if (obj.EmployeeID === ID) {
// parent is obj, add child into its children
obj.employees ? obj.employees.push(employee) : (obj.employees = [employee]);
// indication to stop search further (check loop in else block using Array.some method)
return true;
} else {
const foundParet =
obj.employees &&
obj.employees.some((childObj) =>
findParentAndAppendChild(ID, childObj, employee)
);
return foundParet ? true : false;
}
};
data.forEach((employee) => {
if (employee.ManagerEmployeeID === "") {
// top level employee
nestedObj.employees.push(employee);
} else {
// non top level employee
findParentAndAppendChild(employee.ManagerEmployeeID, nestedObj, employee);
}
});
// log the output
console.log(JSON.stringify(nestedObj.employees, null, 2));