0

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

5

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, ''))

Sign up to request clarification or add additional context in comments.

9 Comments

Thanks, working on trying to integrate this now. For some reason it is throwing ` TypeError: data.reduce is not a function`.
Is data array?
It's an array of objects apparently. (oi63.tinypic.com/2ivd2rt.jpg). Apologies I didn't catch that when I wrote it out, my dev environment is a few versions behind.
Run function with just this code and see if you get error jsfiddle.net/Lg0wyt9u/1907
This returned an empty array []
|
0

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

0

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

I gave this a try but my array is coming up empty. I tried with two employees in this example. A manager, and someone that reports up to them (the employee). jsfiddle.net/pqncv5ny
0

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));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.