2

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.

0

3 Answers 3

1

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

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

6 Comments

Thanks but i donot need mobilePhone & doctorID, so how can we improve the same?
Is there any problem in converting to this? const result = dbResponse.reduce((acc, { attributes: { dob, name }, userID }) => { acc[userID] = { dob, name, userID }; return acc; }, {});
You destructured the properties of the current object. And the finalResult will not contain any data in attributes key as you added them separately. This would work right.
Just a suggestion, add default values as well. For eg: If attributes key does not exist in some object, the code would break.
You meant by checking if condition? or any other good method do we have?
|
1

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

2 Comments

Wow great explaination and fresh pair of eyes. Thanks for your precious time.
Yes, map could be used instead of reduce. Though reduce does give the liberty to control the pushing into the accumulator, which map does not. In terms of performance, reduce and map are kind of similar. One slightly beats another depending upon the operation done in the callback.
1

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

2 Comments

Thanks but i donot need mobilePhone, so i cannot directly pass the attribute itself right?
In that case we can simply copy the relevant properties!

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.