Vishnu
Vishnu

Reputation: 724

Update the array with attributes from different array with same key for both - javascript

I have the following array which is assumed to be large data set.

let response1 = [
  { userID: '2222', dataOne: [ [Object], [Object] ] },
  {
    userID: '6666',
    dataOne: [ [Object] ],
    dataTwo: [ [Object], [Object] ]
  },
  {
    userID: '11111',
    dataOne: [ [Object], [Object] ],
    dataTwo: [ [Object] ]
  },
  { userID: '4586', dataTwo: [ [Object] ] }
];

I have another array which i got as a result of database query (which is also a large data set)

let dbResponse  = [{
  "attributes": {
    "dob": "19890147",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Ketan Hol",
  },
  "doctorID": "ds45ds",
  "userID": "11111"
},
{
  "attributes": {
    "dob": "19890386",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Sachin",
  },
  "doctorID": "erjjkrel",
  "userID": "6666"
},
{
  "attributes": {
    "dob": "19890219",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Vishwas",
  },
  "doctorID": "dfgfdg",
  "userID": "2222"
},
{
  "attributes": {
    "dob": "19890219",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Jis",
  },
  "doctorID": "dfgfdg",
  "userID": "98645"
},
{
  "attributes": {
    "dob": "19890219",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Brad",
  },
  "doctorID": "dfgfdg",
  "userID": "4586"
},
    {
          "attributes": {
            "dob": "19890219",
            "gender": "M",
            "mobilePhone": "1239000000",
            "name": "Brad",
          },
          "doctorID": "dfgfdg",
          "userID": "4586"
        }

];

I need to add the attributes such as dob, name from dbResponse to response1 array based on same userID. All the userID in response1 array should be populated with attributes like dob, name from dbResponse. I am confused on how to perform the below in large data set.

Expected output will be like this:

response1 = [
      { userID: '2222', dataOne: [ [Object], [Object] ], dob: '19890219', name: 'Vishwas' },
      {
        userID: '6666',
        dataOne: [ [Object] ],
        dataTwo: [ [Object], [Object] ],
        dob: '19890386',
        name: 'Sachin'
      },
      {
        userID: '11111',
        dataOne: [ [Object], [Object] ],
        dataTwo: [ [Object] ],
        dob: '19890147',
        name: 'Ketan Hol'
      },
      { userID: '4586', dataTwo: [ [Object] ], dob: '19890219', name: 'Brad' }
    ];

What will be the best way to achieve this using es6 functions for a large data sets? I am new to these es6 functions. Any help would be really appreciated.

Upvotes: 2

Views: 178

Answers (3)

Terry Lennox
Terry Lennox

Reputation: 30685

We can use Array.map to merge the two data sets, also using Array.find to match users from the response1 array with the dbResponse array.

We can then use Object.assign() to copy all properties from the attributes in dbUser to the user object.

let response1 = [ { userID: '2222', dataOne: [ {}, {} ] }, { userID: '6666', dataOne: [ {} ], dataTwo: [ {}, {} ] }, { userID: '11111', dataOne: [ {}, {} ], dataTwo: [ {} ] }, { userID: '4586', dataTwo: [ {} ] } ];
let dbResponse  = [{ "attributes": { "dob": "19890147", "gender": "M", "mobilePhone": "1239000000", "name": "Ketan Hol", }, "doctorID": "ds45ds", "userID": "11111" }, { "attributes": { "dob": "19890386", "gender": "M", "mobilePhone": "1239000000", "name": "Sachin", }, "doctorID": "erjjkrel", "userID": "6666" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Vishwas", }, "doctorID": "dfgfdg", "userID": "2222" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Jis", }, "doctorID": "dfgfdg", "userID": "98645" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Brad", }, "doctorID": "dfgfdg", "userID": "4586" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Brad", }, "doctorID": "dfgfdg", "userID": "4586" }  ];

function mergeUserData(response, dbResponse) {
    return response.map(user => {
        // Find the same user in the dbResponse array 
        let dbUser = dbResponse.find(dbUser => dbUser.userID === user.userID);
        if (dbUser) {
            // Copy the relevant information 
            user.name = dbUser.attributes.name;
            user.dob = dbUser.attributes.dob;
        }
        return user;
    })
}
console.log("Merged data:", mergeUserData(response1, dbResponse));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

danyg
danyg

Reputation: 71

So I've been checking this one, @mappie answer Is quite good the indexed way is the fastest way although the finalResult can be done using map rather than reduce, personally thing map is clearer, and seems similar timings. You can see that using Find is really slow compared to the other 2 solutions.

Here you can find a snippet comparing timeings between methods. @mappie solution but using Map (but there is no check for incosistency between 2 arrays)

const result = dbResponse.reduce((acc, obj) => {
    const { userID } = obj;
    acc[userID] = obj;
    return acc;
  }, {});
  const finalResult = response1.map((user) => ({
    ...user,
    ...result[user.userID].attributes,
  }));

/******************************************************************************
 * Mock Data Creation tools
 *****************************************************************************/
const getRandomBetween = (start, end) =>
  Math.floor(Math.random() * (end - start)) + start;
const getRandomDOB = () =>
  getRandomBetween(1950, 2000).toString() +
  getRandomBetween(10, 12).toString() +
  getRandomBetween(10, 29).toString();
const getRandomGender = () => (getRandomBetween(0, 1) === 0 ? "M" : "F");
const getUserIdGenerator = (state) => () => state.userID++;

const getResponseDocument = (userIdGenerator) => ({
  userID: userIdGenerator(),
  dataOne: "don't care",
});
const getDbResponseDocument = (userIdGenerator) => ({
  attributes: {
    dob: getRandomDOB(),
    gender: getRandomGender(),
    mobilePhone: getRandomBetween(1000000000, 9999999999).toString(),
    name: "Ketan Hol",
  },
  doctorID: "ds45ds",
  userID: userIdGenerator(),
});

/******************************************************************************
 * Mock Data Creation
 *****************************************************************************/
const usersAmount = 50000;
const r1UidGen = getUserIdGenerator({ userID: 10000 });
const response1 = Array.from(Array(usersAmount).keys()).map(() =>
  getResponseDocument(r1UidGen)
);
const dbRUidGen = getUserIdGenerator({ userID: 10000 });
const dbResponse = Array.from(Array(usersAmount).keys()).map(() =>
  getDbResponseDocument(dbRUidGen)
);

/******************************************************************************
 * Different ways to merge the arrays
 *****************************************************************************/

function methodIndexed() {
  const result = dbResponse.reduce((acc, obj) => {
    const { userID } = obj;
    acc[userID] = obj;
    return acc;
  }, {});
  const finalResult = response1.reduce((acc, curr) => {
    const { userID } = curr;
    const dbObj = result[userID] || {};
    acc.push({
      ...curr,
      ...dbObj.attributes,
    });
    return acc;
  }, []);
  return finalResult;
}

function methodIndexedMap() {
  const usersById = dbResponse.reduce((acc, obj) => {
    const { userID } = obj;
    acc[userID] = obj;
    return acc;
  }, {});
  const finalResult = response1.map((user) => ({
    ...user,
    ...usersById[user.userID].attributes,
  }));
  return finalResult;
}

const byUserId = (userId) => (item) => item.userID === userId;
function methodFind() {
  return response1.map((user) => ({
    ...user,
    ...dbResponse.find(byUserId(user.userID)).attributes,
  }));
}
const results = [];

/******************************************************************************
 * Test Methods
 *****************************************************************************/
function testMethod(name, method) {
  const title = `Method: "${name}"`;
  console.time(title);
  const result = method();
  console.timeEnd(title);
  // assert data validity
  if (
    !(
      result[42].userID === response1[42].userID &&
      result[42].userID === dbResponse[42].userID &&
      result[42].dob === dbResponse[42].attributes.dob
    )
  ) {
    throw Error(`Method "${name}" does not produce expected output`);
  }
}

// difference between these two are too tight to V8 Runtime Optimizations
// so we run them a few time to stabilize
for (let i = 0; i < 10; i++) {
  testMethod("Indexed", methodIndexed);
  testMethod("Indexed Map", methodIndexedMap);
}

testMethod("Using Find", methodFind);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

mappie
mappie

Reputation: 502

Approach1

Iterate dbResponse for every userId in response1, extract the object and copy the object in response1.

Approach2 (Optimised operation)

As both are large arrays, you will have to iterate dbResponse a large number of times. To optimize the operation of finding the response1 corresponding userID object in the dbResponse array, you could maintain a mapping to reduce the searching complexity.

const result = dbResponse.reduce((acc, obj) => {
    const { userID } = obj
    acc[userID] = obj;
    return acc;
}, {});
const finalResult = response1.reduce((acc, curr) => {
    const { userID } = curr
    const dbObj = result[userID] || {}
    acc.push({
        ...curr,
        ...dbObj
    })
    return acc;
}, []);

The final result will be in finalResult

Upvotes: 1

Related Questions