4

I've got two arrays that have multiple objects

[
     {
         "name":"paul",
         "employee_id":"8"
     }
]

[
     {
         "years_at_school": 6,
         "department":"Mathematics",
         "e_id":"8"
     }
]

How can I achieve the following with either ES6 or Lodash?

[
     {
         "name":"paul",
         "employee_id":"8"
         "data": {
             "years_at_school": 6
             "department":"Mathematics",
             "e_id":"8"
         }
     }
]

I can merge but I'm not sure how to create a new child object and merge that in.

Code I've tried:

school_data = _.map(array1, function(obj) {
    return _.merge(obj, _.find(array2, {employee_id: obj.e_id}))
})

This merges to a top level array like so (which is not what I want):

{
     "name":"paul",
     "employee_id":"8"
     "years_at_school": 6
     "department":"Mathematics",
     "e_id":"8"
 }

The connector between these two is "employee_id" and "e_id".

It's imperative that it's taken into account that they could be 1000 objects in each array, and that the only way to match these objects up is by "employee_id" and "e_id".

tokland
  • 66,169
  • 13
  • 144
  • 170
  • possible answered here - https://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically – SOS9GS Sep 29 '18 at 04:00

5 Answers5

1

In order to match up employee_id and e_id you should iterate through the first array and create an object keyed to employee_id. Then you can iterate though the second array and add the data to the particular id in question. Here's an example with an extra item added to each array:

let arr1 = [
    {
        "name":"mark",
        "employee_id":"6"
    },
    {
        "name":"paul",
        "employee_id":"8"
    }
]

let arr2 = [
    {
        "years_at_school": 6,
        "department":"Mathematics",
        "e_id":"8"
    },
    {
        "years_at_school": 12,
        "department":"Arr",
        "e_id":"6"
    }
    
]

// empObj will be keyed to item.employee_id 
let empObj = arr1.reduce((obj, item) => {
    obj[item.employee_id] = item
    return obj
}, {})

//  now lookup up id and add data for each object in arr2
arr2.forEach(item=>
    empObj[item.e_id].data = item
)

// The values of the object will be an array of your data
let merged = Object.values(empObj)
console.log(merged)
Mark
  • 90,562
  • 7
  • 108
  • 148
  • Hi @Mark Meyer, The objects aren't always aligned, they have to be merged on the keys 'employee_id' & 'e_id'. – Paul Simmons Sep 29 '18 at 04:09
  • Hi @PaulSimmons, you have two arrays. By aligned I mean you want the data in `arr1[n]` to be merged with `arr2[n]`. If `arr2` was in a different order it wouldn't work. – Mark Sep 29 '18 at 04:11
  • Sorry @Mark Meyer, I'm confused on the aligned part. My main concern is that if there's an "employee_id" of 6, arr2 isn't going to go in there because it has an "e_id" of value 8. – Paul Simmons Sep 29 '18 at 04:15
  • @PaulSimmons, sorry I didn't notice the e_id -> employee_id relation. I've edit the answer. I think it's closer to what you are looking for. It shouldn't care about the array order. – Mark Sep 29 '18 at 04:38
1

If you perform two nested O(n) loops (map+find), you'll end up with O(n^2) performance. A typical alternative is to create intermediate indexed structures so the whole thing is O(n). A functional approach with lodash:

const _ = require('lodash');
const dataByEmployeeId = _(array2).keyBy('e_id');
const result = array1.map(o => ({...o, data: dataByEmployeeId.get(o.employee_id)}));
tokland
  • 66,169
  • 13
  • 144
  • 170
0

Hope this help you:

var mainData = [{
   name: "paul",
   employee_id: "8"
}];

var secondaryData = [{
  years_at_school: 6,
  department: "Mathematics",
  e_id: "8"
}];

var finalData = mainData.map(function(person, index) {
  person.data = secondaryData[index];
  return person;
});

Sorry, I've also fixed a missing coma in the second object and changed some other stuff.

With latest Ecmascript versions:

const mainData = [{
   name: "paul",
   employee_id: "8"
}];

const secondaryData = [{
  years_at_school: 6,
  department: "Mathematics",
  e_id: "8"
}];

// Be careful with spread operator over objects.. it lacks of browser support yet! ..but works fine on latest Chrome version for example (69.0)
const finalData = mainData.map((person, index) => ({ ...person, data: secondaryData[index] }));
Juan Elfers
  • 770
  • 7
  • 13
0

A slightly different approach just using vanilla js map with a loop to match the employee ids and add the data from the second array to the matching object from the first array. My guess is that the answer from @MarkMeyer is probably faster.

const arr1 = [{ "name": "paul", "employee_id": "8" }];
const arr2 = [{ "years_at_school": 6, "department": "Mathematics", "e_id": "8" }];
const results = arr1.map((obj1) => {
  for (const obj2 of arr2) {
    if (obj2.e_id === obj1.employee_id) {
      obj1.data = obj2;
      break;
    }
  }
  
  return obj1;
});

console.log(results);
benvc
  • 14,448
  • 4
  • 33
  • 54
0

Your question suggests that both arrays will always have the same size. It also suggests that you want to put the contents of array2 within the field data of the elements with the same index in array1. If those assumptions are correct, then:

// Array that will receive the extra data
const teachers = [
    { name: "Paul", employee_id: 8 },
    { name: "Mariah", employee_id: 10 }
];

// Array with the additional data
const extraData = [
    { years_at_school: 6, department: "Mathematics", e_id: 8 },
    { years_at_school: 8, department: "Biology", e_id: 10 },
];

// Array.map will iterate through all indices, and gives both the
const merged = teachers.map((teacher, index) => Object.assign({ data: extraData[index] }, teacher));

However, if you want the data to be added to the employee with an "id" matching in both arrays, you need to do the following:

// Create a function to obtain the employee from an ID
const findEmployee = id => extraData.filter(entry => entry.e_id == id);

merged = teachers.map(teacher => {
    const employeeData = findEmployee(teacher.employee_id);

    if (employeeData.length === 0) {
        // Employee not found
        throw new Error("Data inconsistency");
    }

    if (employeeData.length > 1) {
        // More than one employee found
        throw new Error("Data inconsistency");
    }

    return Object.assign({ data: employeeData[0] }, teacher);
});
Pedro Batista
  • 161
  • 1
  • 6