简体   繁体   中英

mapping JSON Data reverse?

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" eg

[{
  "_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...

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) 

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

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

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM