2

I got stuck on a maybe simple task, but could not find any solution. I have some JSON Data - lets say:

[{
  "_id": 1,
  "type": "person",
  "Name": "Hans",
  "WorksFor": ["3", "4"]
}, {
  "_id": 2,
  "type": "person",
  "Name": "Michael",
  "WorksFor": ["3"]
}, {
  "_id": 3,
  "type": "department",
  "Name": "Marketing"
}, {
  "_id": 4,
  "type": "department",
  "Name": "Sales"
}]

As I learned here it is quite simple to get all the persons and the departments they work for together using a map array for the departments. Then I can map the corresponding department to the Person and receive something like:

[{
  "_id": 1,
  "type": "person",
  "Name": "Hans",
  "WorksFor": ["3", "4"],
  "Readable": ["Marketing", "Sales"]
}, {
  "_id": 2,
  "type": "person",
  "Name": "Michael",
  "WorksFor": ["3"],
  "Readable": ["Sales"]
}]

But for another interface I need the data "the other way round" e.g.

[{
  "_id": 3,
  "type": "department",
  "Name": "Marketing",
  "employees": [
        "Hans", "Michael"
    ]
}, {
  "_id": 4,
  "type": "department",
  "Name": "Sales",
  "employees": [
        "Hans"
    ]
  }]

Is there any decent way to achieve this structure? Two days of trying didn't get me anywhere...

1
  • Did you write any code? Commented Feb 3, 2017 at 8:17

6 Answers 6

1

var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"] }, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"] }, { "_id": 3, "type": "department", "Name": "Marketing" }, { "_id": 4, "type": "department", "Name": "Sales" }];

var departments = [],
    persons = [];

data.forEach(e => {
  if (e.type === "person") {
    persons.push(e);
  } else if (e.type === "department") {
    departments.push(e);
    e.employees = [];
  }
});

departments.forEach(d => {
  var workers = persons.filter(p => p.WorksFor.indexOf(d._id.toString()) > -1)
                     /*.map(p => p.Name)*/    // add this if you only need the name instead of the complete "person"

  d.employees = d.employees.concat(workers);
});

console.log(JSON.stringify(departments, null, 4));

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

Comments

1

You can try something like this:

var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"]}, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"]}, { "_id": 3, "type": "department", "Name": "Marketing"}, { "_id": 4, "type": "department", "Name": "Sales"}]
var ignoreDept = ['person'];

var result = data.reduce(function(p,c,i,a){
  if(ignoreDept.indexOf(c.type) < 0){
    c.employees = a.reduce(function(arr,emp){
      if(emp.WorksFor && emp.WorksFor.indexOf(c._id.toString()) > -1){
        arr.push(emp.Name)
      }
      return arr;
    },[]);
    p.push(c);
  }
  return p;
}, []);

console.log(result)

Comments

1

The solution using Array.prototype.filter() and Array.prototype.forEach() functions:

var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"]}, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"]}, { "_id": 3, "type": "department", "Name": "Marketing"}, { "_id": 4, "type": "department", "Name": "Sales"}],
    // getting separated "lists" of departments and employees(persons) 
    deps = data.filter(function(o){ return o.type === "department"; }),
    persons = data.filter(function(o){ return o.type === "person"; });
    
deps.forEach(function (d) {
  d['employees'] = d['employees'] || [];
  persons.forEach(function (p) {
      if (p.WorksFor.indexOf(String(d._id)) !== -1) {  // check the `id` coincidence between the employee and the department
        d['employees'].push(p.Name);
      }
  });
});
    
console.log(deps);

Comments

1

You could use a hash table and a single loop for each array.

Methods:

  • Array#reduce for iterating an array and returning the result,
  • Array#forEach for looping the inner array WorksFor,
  • Object.create(null) to generate an object without any prototypes,
  • some other pattern, like a closure over hash and
  • the use of logical OR || for checking a falsy value and taking an object as default.

    hash[b] = hash[b] || { _id: b, employees: [] };
    

var data = [{ _id: 1, type: "person", Name: "Hans", WorksFor: [3, 4] }, { _id: 2, type: "person", Name: "Michael", WorksFor: [3] }, { _id: 3, type: "department", Name: "Marketing" }, { _id: 4, type: "department", Name: "Sales" }],
    result = data.reduce(function (hash) {
        return function (r, a) {
            if (a.type === 'person') {
                a.WorksFor.forEach(function (b) {
                    hash[b] = hash[b] || { _id: b, employees: [] };
                    hash[b].employees.push(a.Name);
                });
            }
            if (a.type === 'department') {
                hash[a._id] = hash[a._id] || { _id: b, employees: [] };
                hash[a._id].type = a.type;
                hash[a._id].Name = a.Name;
                r.push(hash[a._id]);
            }
            return r;
        };
    }(Object.create(null)), []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

Here's a way you can get the first mapping. I've added some comments so you can follow along, and with it I hope you can find the answer to your second problem.

// First, let's get just the items in this array that identify persons
// I've called this array "data"
data.filter(x => x.type === 'person') 
    // Now let's map over them
    .map(person =>
      // We want all of the data associated with this person, so let's
      // use Object.assign to duplicate that data for us
      Object.assign({}, person, {
          // In addition, we want to map the ID of the WorksFor array to the Name
          // of the corresponding department. Assuming that the _id key is unique,
          // we can due this simply by mapping over the WorksFor array and finding
          // those values within the original array.
          Readable: person.WorksFor.map(wfId =>
             // Notice here the parseInt. This will not work without it due to
             // the type difference between WorksFor (string) and _id (integer)
             data.find(d => d._id === parseInt(wfId)).Name
          )
      })
);

Comments

0

var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"]}, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"]}, { "_id": 3, "type": "department", "Name": "Marketing"}, { "_id": 4, "type": "department", "Name": "Sales"}];

var dep = {};

data.forEach(e => (e.type === 'person' && e.WorksFor.forEach(d => dep[d]? dep[d].push(e.Name): dep[d] = [e.Name])));

data.forEach(e => (e.type == 'department' && (e.employees = dep[e._id] || [])));

data = data.filter(e => e.type == 'department');

console.log(data);

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.