1

I am trying to combine two object based on keys given But, It become hard then expected.

const keys = ["year_and_month", "name"];
const firstData = [
    {
      "year_and_month": "2022-01",
      "name": "Roy",
      "data1": 3388560126.065652,
      "data2": 889386921115.6444
    },
    {
      "year_and_month": "2022-02",
      "name": "Roy",
      "data1": 3897475493.9987683,
      "data2": 874308539354.9275
    },
    {
      "year_and_month": "2022-02",
      "name": "Dan",
      "data1": 443961561.24720746,
      "data2": 293742072318.20105
    },
    {
      "year_and_month": "2022-01",
      "name": "Dan",
      "data1": 362316175.165702,
      "data2": 303232994015.44
    },
    {
      "year_and_month": "2022-02",
      "name": "Tata",
      "data1": 22744562.245873,
      "data2": 790044572051.9896
    },
    {
      "year_and_month": "2022-01",
      "name": "Tata",
      "data1": 22744562.245873,
      "data2": 790044572051.9896
    }
  ]
  
  const secondData = [
    {
      "year_and_month": "2022-01",
      "name": "Roy",
      "data3": 4083741657.64,
      "data4": 36693935225.96
    },
    {
      "year_and_month": "2022-02",
      "name": "Roy",
      "data3": 2436561209.83,
      "data4": 31013728462.63
    },
    {
      "year_and_month": "2022-02",
      "name": "Dan",
      "data3": -8298497218.39,
      "data4": 34817072736.04
    },
    {
      "year_and_month": "2022-01",
      "name": "Dan",
      "data3": 1312315161.48,
      "data4": 34790053142.56
    },
    {
      "year_and_month": "2022-01",
      "name": "Tata",
      "data3": -745507724.01,
      "data4": 13533928780.38
    },
    {
      "year_and_month": "2022-02",
      "name": "Tata",
      "data3": 658173707,
      "data4": 13282740718.8
    }
  ]


//This way, don't find the way to move forward

//let firstKeys = [];
//let secondKeys = [];
//firstData.map(data => keys.forEach(key => firstKeys.push(data[key]) ));
//secondData.map(data => keys.forEach(key => secondKeys.push(data[key]) ));

//console.log(firstKeys);
//console.log(secondKeys);


//Tried Second way But, stuck again
const result = secondData.reduce((res, ele) => {
  console.log(ele);
  keys.forEach(key => {
    firstData.find(data => data[key] === ele[key]);
   })
   
  return res
}, [])


console.log("result", result);

Expected Result (combine both array based on keys given like here match year_and_month and name):

  [{
      "year_and_month": "2022-01",
      "name": "Roy",
      "data1": 3388560126.065652,
      "data2": 889386921115.6444,
      "data3": 4083741657.64,
      "data4": 36693935225.96
    },
    {
      "year_and_month": "2022-02",
      "name": "Roy",
      "data1": 3897475493.9987683,
      "data2": 874308539354.9275,
      "data3": -8298497218.39,
      "data4": 34817072736.04
    }
    ... and so on
  ]

5 Answers 5

2

Try if this helps:

const combinedData = firstData.map(item => {
    return {
      ...item,
      ...(secondData.find(secondItem => secondItem.year_and_month === item.year_and_month && secondItem.name === item.name))
    }
  })
Sign up to request clarification or add additional context in comments.

3 Comments

There can be possible that keys can be more than or less than 2. So, should I iterate over keys before return?
@ketan do you mean ...(secondData.find(secondItem => keys.every((key) => secondItem[key] === item[key])))
I think you missed "based on keys given" part. You should consider @cmgchess's suggestion
2

If your two arrays have the same length and are sorted in the same way, you can combine the nested objects with Object.assign:

const result = firstData.map((obj, i) => (Object.assign(obj, secondData[i])));

Comments

2
const result = firstData.map((firstEl) => {
  const secondEl = secondData.find((el) =>
    keys.every((key) => firstEl[key] === el[key])
  );
  return { ...firstEl, ...secondEl };
});

Explanation: Map all the elements from firstData, find their match (that has the same values in given keys) in secondData and merge them using spread operator (...)

Note: Be careful on comparing non-primitive values.

Comments

1

Here's a possible approach that relies on the supplied keys being strings. You could modify the extractKey function as required. I've written it to take any number of keys, but if you only have two it could obviously be simplified.

This approach also does not make any attempt to sort the incoming fields or sort the output according to name and date.

Finally, there are definitely some optimizations that could be made, including memoizing the key computation if you had a large dataset, and storing those in a hash table.

const keys = ["year_and_month", "name"];
const firstData = [{
    "year_and_month": "2022-01",
    "name": "Roy",
    "data1": 3388560126.065652,
    "data2": 889386921115.6444
  },
  {
    "year_and_month": "2022-02",
    "name": "Roy",
    "data1": 3897475493.9987683,
    "data2": 874308539354.9275
  },
  {
    "year_and_month": "2022-02",
    "name": "Dan",
    "data1": 443961561.24720746,
    "data2": 293742072318.20105
  },
  {
    "year_and_month": "2022-01",
    "name": "Dan",
    "data1": 362316175.165702,
    "data2": 303232994015.44
  },
  {
    "year_and_month": "2022-02",
    "name": "Tata",
    "data1": 22744562.245873,
    "data2": 790044572051.9896
  },
  {
    "year_and_month": "2022-01",
    "name": "Tata",
    "data1": 22744562.245873,
    "data2": 790044572051.9896
  }
]

const secondData = [{
    "year_and_month": "2022-01",
    "name": "Roy",
    "data3": 4083741657.64,
    "data4": 36693935225.96
  },
  {
    "year_and_month": "2022-02",
    "name": "Roy",
    "data3": 2436561209.83,
    "data4": 31013728462.63
  },
  {
    "year_and_month": "2022-02",
    "name": "Dan",
    "data3": -8298497218.39,
    "data4": 34817072736.04
  },
  {
    "year_and_month": "2022-01",
    "name": "Dan",
    "data3": 1312315161.48,
    "data4": 34790053142.56
  },
  {
    "year_and_month": "2022-01",
    "name": "Tata",
    "data3": -745507724.01,
    "data4": 13533928780.38
  },
  {
    "year_and_month": "2022-02",
    "name": "Tata",
    "data3": 658173707,
    "data4": 13282740718.8
  }
]

// You need a function that will extract a unique and stable key 
// for each combination of name and date.
function extractKey(obj, keys) {
  return keys.reduce((compoundKey, key) => {
    return compoundKey + obj[key];
  }, "");
}

// Iterate over all the data, binning the objects by their compound keys.
// Your results will be the array of values.
const results = Object.values([...firstData, ...secondData].reduce((map, datum) => {
  const key = extractKey(datum, keys);
  if (map[key]) {
    // In the case that we've seen this key already,
    // the object spread merges the fields from the 
    // current object into the existing data.
    map[key] = {...map[key], ...datum};
  } else {
    // If we haven't seen this key before, we just 
    // store the current object as the only entry for
    // that name and date.
    map[key] = {...datum};
  }
  return map;
}, {}));

console.log(results);

Comments

1

This is a good use case for the built-in Map data structure, in whose key-value pairs we can have strings as keys.

The idea is to conceptually treat the pair (year_and_month, name) as a composite primary key, then turn it into a simple key to be used in a new Map entry whose value gets updated with more "data fields" (data1, data2, etc.) as we iterate over the Array inputs and find matching keys.

In order to update such a Map entry, we'll use spread operations inside a new object literal, then pass it to a call to Map.prototype.set().

In the end, the Map values will have the information we want, which can be coerced back into an Array.

/*
O(n)
Concatenates arrays, makes one pass & creates map entries as it goes. 
Uses existing (year_and_month, name) pairs as map keys.
Finally, returns an array made from the map values.
*/
function mergeArrays(firstArray, secondArray) {
  const map = new Map()

  // for...of statement over Array, spread operator
  for (const arrayValue of [...firstArray, ...secondArray]) {
    const {
      year_and_month,
      name
    } = arrayValue // destructuring assignment
    const mapKey = [year_and_month, name].join(',')
    const mapValue = map.get(mapKey)

    map.set(mapKey, { ...mapValue,
      ...arrayValue
    }) // spread operator
  }

  return Array.from(map.values())
}

const firstData = [{
    year_and_month: '2022-01',
    name: 'Roy',
    data1: 3388560126.065652,
    data2: 889386921115.6444,
  },
  {
    year_and_month: '2022-02',
    name: 'Roy',
    data1: 3897475493.9987683,
    data2: 874308539354.9275,
  },
  {
    year_and_month: '2022-02',
    name: 'Dan',
    data1: 443961561.24720746,
    data2: 293742072318.20105,
  },
  {
    year_and_month: '2022-01',
    name: 'Dan',
    data1: 362316175.165702,
    data2: 303232994015.44,
  },
  {
    year_and_month: '2022-02',
    name: 'Tata',
    data1: 22744562.245873,
    data2: 790044572051.9896,
  },
  {
    year_and_month: '2022-01',
    name: 'Tata',
    data1: 22744562.245873,
    data2: 790044572051.9896,
  },
]

const secondData = [{
    year_and_month: '2022-01',
    name: 'Roy',
    data3: 4083741657.64,
    data4: 36693935225.96,
  },
  {
    year_and_month: '2022-02',
    name: 'Roy',
    data3: 2436561209.83,
    data4: 31013728462.63,
  },
  {
    year_and_month: '2022-02',
    name: 'Dan',
    data3: -8298497218.39,
    data4: 34817072736.04,
  },
  {
    year_and_month: '2022-01',
    name: 'Dan',
    data3: 1312315161.48,
    data4: 34790053142.56,
  },
  {
    year_and_month: '2022-01',
    name: 'Tata',
    data3: -745507724.01,
    data4: 13533928780.38,
  },
  {
    year_and_month: '2022-02',
    name: 'Tata',
    data3: 658173707,
    data4: 13282740718.8,
  },
]

console.log(mergeArrays(firstData, secondData))

I ran all the answers through 100.000 sized inputs and got the following execution times on my laptop:

    61ms for call to david
   284ms for call to lucianoFerraz
   294ms for call to mattMorgan
 47641ms for call to apurvaMistry
 48969ms for call to yuceKilic

David's answer is by far the fastest, but unreliable if you can't guarantee that your array inputs will be symmetrical and equally ordered.

Mine and Matt's run in the middle with the same time complexity @ O(n), but without David's compromises.

Apurva & Yüce's take the longest to complete because they run @ O(n^2).

I hope that helps!

1 Comment

I like the fact that you've called out the big O performance of various solutions in your answer :) I think you should amend this statement: For that purpose, Map.prototype.set() will merge all array entries that generate the same string key.. Map.set doesn't do that, the object spread does. Map.set just puts the merged value into the map.

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.