0

I have couple of arrays that contain individual objects as represented below. I'd like to merge these 2 structures in a single one:

Changes

An array of changes which definition is:

{ 
  app_name: "App 1"
  2017: {fYear: 2017, changesCount: 37}, 
  2018: {fYear: 2018, changesCount: 10}
}

Issues

An array of issues which definition is

{ 
  app_name: "App 1",
  2018: {fYear: 2018, all: 10, typeA: 1, typeB:5, TypeC: 1}
}

End result

Looking for an array of merged objects, each object being the merge of a Change and an Issue:

{ 
  app_name: "App 1"
  2017: {fYear: 2017, changesCount: 37}, 
  2018: {fYear: 2018, changesCount: 10, all: 10, typeA: 1, typeB:5, TypeC: 1}
}

I can do a loop of course but seems not the best to me so I've been trying to look into the map & reduce functions but so far no luck.

Any recommendation?

Thank you!

goul
  • 813
  • 1
  • 13
  • 32
  • Have a look at `Object.keys` – JanS Dec 17 '18 at 07:46
  • 1
    You will have to loop over the two arrays in any case - whether you use for loop explicitly or a combination of map/reduce (which internally iterate over the elements themselves too). What was the challenge with using map/reduce? Can you share code that you have tried? – Chirag Ravindra Dec 17 '18 at 07:47
  • Understood. Will loop but at least don't want to consider the 3 cases of 1) entry existing in Change but not in Issue, 2) entry in both, 3) entry in Issue but not in change – goul Dec 17 '18 at 07:49
  • Are `app_name` keys unique in each array? And is using external libs like `lodash` or `underscore` which provide a recursive merge function by default an option? – Chirag Ravindra Dec 17 '18 at 07:54
  • 1
    Actually if you check the polyfill of `Array.prototype.map()` [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), you still find a while loop inside. – devildelta Dec 17 '18 at 07:57
  • Yes they are unique. I was trying to avoid the libraries but might do then. I've seen a comment from someone that just disappeared, it was nice, ie. recommending to use Object.assign() though not working for properties not in both objects it seems – goul Dec 17 '18 at 07:58

2 Answers2

1
let a = {
  app_name: "App 1",
  '2017': { fYear: 2017, changesCount: 37 },
  '2018': { fYear: 2018, changesCount: 10 }
}
let b = {
  app_name: "App 1",
  2018: { fYear: 2018, all: 10, typeA: 1, typeB: 5, TypeC: 1 }
}

let c = { ...a, ...b };
let d = Object.keys(c).reduce((result, item) => {
  if (a[item] && typeof a[item] === 'object') {
    Object.assign(result[item], a[item])
  }
  return result;
}, c);
陶Charon
  • 11
  • 1
1

I can think of a few ways to do this:

1) Using Map on one array (the superset) and use Find inside to find the matching element in the other array. One array must be a superset and app_name must be unique within both arrays for this to work. (You could change find to filter if the app_name is not unique)

2) Is more general: Create an object map with keys as app_name and augment fields from both arrays.

Example Using The Object Map Method

let changes = [{
  app_name: "App 1",
  2017: {
    fYear: 2017,
    changesCount: 37
  },
  2018: {
    fYear: 2018,
    changesCount: 10
  }
}]

let issues = [{
  app_name: "App 1",
  2018: {
    fYear: 2018,
    all: 10,
    typeA: 1,
    typeB: 5,
    TypeC: 1
  }
}]
/*
Expected Result:
{ 
  app_name: "App 1"
  2017: {fYear: 2017, changesCount: 37}, 
  2018: {fYear: 2018, changesCount: 10, all: 10, typeA: 1, typeB:5, TypeC: 1}
}*/

function merge(arr1, arr2) {

  // Keep an object to accumulate the keys onto
  const acc = {};

  // Helper to register array items on to the accumulator
  function registerToMap(array) {
    array.forEach(function(item) {
      let app_name = item.app_name;
      if (!acc[app_name]) {
        // This app_name is not in the accumulator yet
        // Delete this key to make sure our merge later does not operate on this key
        delete item.app_name;
        acc[app_name] = item;
        return;
      }

      // Merge inner keys of incoming object
      Object.keys(item).forEach(function(key) {
        acc[app_name][key] = Object.assign({}, acc[app_name][key], item[key])
      })
      
      
      // The above steps could be easier using a lib with a proper merge function
      // acc[app_name] = _.merge({}, item, acc[app_name])

    })
  }
  
  // Register the first array elements onto the accumulator
  registerToMap(arr1);
  
  // Register the second array elements onto the accumulator
  registerToMap(arr2);

  // Rebuild array
  return Object.keys(acc).map(function(key) {
    return Object.assign({}, acc[key], {
      app_name: key
    });
  })

}

console.log(merge(changes, issues))
Chirag Ravindra
  • 4,760
  • 1
  • 24
  • 35