1

I have two arrays originalArray and modifiedArray which have objects with some properties; sys_id is the unique property:

originalArray = [
    { sys_id: 1234, type: 'XYZZ' }, 
    { sys_id: 1235, type: 'ABCD' }, 
    { sys_id: 1236, type: 'IJKL' },
    { sys_id: 1237, type: 'WXYZ' }, 
    { sys_id: 1238, type: 'LMNO' }
]

modifiedArray = [
    { sys_id: 1234, type: 'XYZZ' }, 
    { sys_id: 1235, type: 'ZZAA' },  
    { sys_id: 1236, type: 'ZZZZ' },
    { sys_id: 1252, type: 'AAAA' }
]

I'm looking to combine/merge the arrays but include a new property that describes a full account of what changed in the new array based on the original array using the sys_id property.

resultingArray = [
    { sys_id: 1234, type: 'XYZZ', action: 'same' }, 
    { sys_id: 1235, type: 'ZZAA', action: 'edit' }, 
    { sys_id: 1236, type: 'ZZZZ', action: 'edit' },
    { sys_id: 1237, type: 'WXYZ', action: 'remove' }, 
    { sys_id: 1238, type: 'LMNO', action: 'remove' },
    { sys_id: 1252, type: 'AAAA', action: 'add' }

Also would like to know if there is more proper terminology, or a more concise way to explain what I'm trying to accomplish here?

I'm on a platform that is limited to ES5.

Ryan Litwiller
  • 497
  • 5
  • 24

5 Answers5

1

Iterate over the first array, find the corresponding value in second and based on that generate element. Finally, add remaining elements as newly added.

var originalArray = [{      sys_id: 1234,      type: 'XYZZ'    },    {      sys_id: 1235,      type: 'ABCD'    },    {      sys_id: 1236,      type: 'IJKL'    },    {      sys_id: 1237,      type: 'WXYZ'    },    {      sys_id: 1238,      type: 'LMNO'    }  ],
  modifiedArray = [{      sys_id: 1234,      type: 'XYZZ'    },    {      sys_id: 1235,      type: 'ZZAA'    },    {      sys_id: 1236,      type: 'ZZZZ'    },    {      sys_id: 1252,      type: 'AAAA'    }  ];
  
  
// keep a swallow copy to not effect the original one
let mA = modifiedArray.slice();

// iterate over to generate new array
let res = originalArray.map(o => {
  // get index of eleemnt
  let moi = mA.findIndex(o1 => o1.sys_id === o.sys_id); 
  // if found 
  if (moi > -1) {
    // remove it from the swallow copied array
    let mo = mA.splice(moi,1)[0];
    // check and generate new array
    return { ...mo, action: mo.type === o.type ? 'same' : 'edited' }
  } else {
    // if not found return as removed
    return { ...o, action: 'removed' }
  }
  // add remaining values as added
}).concat(mA.map(o=>({...o, action: 'added'})))

console.log(res);

ES5 alternative:

var originalArray = [{      sys_id: 1234,      type: 'XYZZ'    },    {      sys_id: 1235,      type: 'ABCD'    },    {      sys_id: 1236,      type: 'IJKL'    },    {      sys_id: 1237,      type: 'WXYZ'    },    {      sys_id: 1238,      type: 'LMNO'    }  ],
  modifiedArray = [{      sys_id: 1234,      type: 'XYZZ'    },    {      sys_id: 1235,      type: 'ZZAA'    },    {      sys_id: 1236,      type: 'ZZZZ'    },    {      sys_id: 1252,      type: 'AAAA'    }  ];


// function for copying properties to function
function copyProperty(from, to) {
  for (var prop in from) {
    if (from.hasOwnProperty(prop))
      to[prop] = from[prop];
  }
  return to;
}

var mA = modifiedArray.slice();

var res = originalArray.map(function(o) {
  var moi = -1;
  
  // get index by iterating
  for (var i = 0; i < mA.length; i++) {
    if (mA[i].sys_id === o.sys_id) {
      moi = i;
      break;
    }
  }
  if (moi != -1) {
    var mo = mA.splice(moi, 1)[0];
    return copyProperty(mo, { action: mo.type === o.type ? 'same' : 'edited' })
  } else {
    return copyProperty(o, { action: 'removed' })
  }
})

// push remaining values
mA.forEach(function(o) {
  res.push(copyProperty(o, { action: 'added' }))
})

console.log(res);
Pranav C Balan
  • 113,687
  • 23
  • 165
  • 188
1

Using Array#reduce() to make object that uses sys_id as keys and has properties orig and mod then a good old for in loop to iterate that object and compare the 2 types when they exist or check which doesn't exist and push proper data to results array.

All ES5 compatible

var grouped = [originalArray, modifiedArray].reduce(function(acc, arr, i) {
  var label = i === 0 ? 'orig' : 'mod';
  for (var j = 0; j < arr.length; j++) {
    var curr = arr[j], id = curr.sys_id;
    acc[id] = acc[id] || {orig: null, mod: null };
    acc[id][label] = curr;
  }
  return acc;
}, {});

var res = [];

function insertObj(o, act) {
  var newObj = { action: act };
  // iterate existing keys to add to new object
  for (var k in o) {
    if (o.hasOwnProperty(k)) {
      newObj[k] = o[k]
    }
  }
  res.push(newObj)
}

for (var key in grouped) {
  var action, obj;
  if (grouped.hasOwnProperty(key)) { 
    obj = grouped[key]
    if (!obj.orig) {
      insertObj(obj.mod, 'add');
    } else if (!obj.mod) {
      insertObj(obj.orig, 'remove')
    } else {
      action = obj.mod.type === obj.orig.type ? 'same' : 'edit';
      insertObj(obj.mod, action)
    }
  }
}

console.log(res)
<script>

var originalArray = [
    { sys_id: 1234, type: 'XYZZ' }, 
    { sys_id: 1235, type: 'ABCD' }, 
    { sys_id: 1236, type: 'IJKL' },
    { sys_id: 1237, type: 'WXYZ' }, 
    { sys_id: 1238, type: 'LMNO' }
]

var modifiedArray = [
    { sys_id: 1234, type: 'XYZZ' }, 
    { sys_id: 1235, type: 'ZZAA' },  
    { sys_id: 1236, type: 'ZZZZ' },
    { sys_id: 1252, type: 'AAAA' }
]
</script>
charlietfl
  • 170,828
  • 13
  • 121
  • 150
1

Nothing but the good old brute force way of doing things. Also, it is important to ensure that there is no mutation to the original inputs. Basically, I loop through everything twice, and check for the following conditions:

1) same sys_id and type (action: same)

2) same sys_id, different type (action: edit)

3) different sys_id, different type (action: delete)

4) different sys_id, different type, and the object exists on modifiedArray but does not exist on originalArray (action: add)

(It pains me to write this without any ES6)

var originalArray = [
    { sys_id: 1234, type: 'XYZZ' }, 
    { sys_id: 1235, type: 'ABCD' }, 
    { sys_id: 1236, type: 'IJKL' },
    { sys_id: 1237, type: 'WXYZ' }, 
    { sys_id: 1238, type: 'LMNO' }
];

var modifiedArray = [
    { sys_id: 1234, type: 'XYZZ' }, 
    { sys_id: 1235, type: 'ZZAA' },  
    { sys_id: 1236, type: 'ZZZZ' },
    { sys_id: 1252, type: 'AAAA' }
];

var resultingArray = [];

for (var i = 0; i < originalArray.length; i++) {
  for (var j = 0; j < modifiedArray.length; j++) {
    if (originalArray[i].sys_id === modifiedArray[j].sys_id && originalArray[i].type === modifiedArray[j].type) {
      resultingArray.push({
        sys_id: originalArray[i].sys_id,
        type: originalArray[i].type,
        action: 'same'
      });
      break;
    } else if (originalArray[i].sys_id === modifiedArray[j].sys_id && originalArray[i].type !== modifiedArray[j].type) {
      resultingArray.push({
        sys_id: originalArray[i].sys_id,
        type: modifiedArray[j].type,
        action: 'edit'
      });
      break;
    } else if (originalArray[i].sys_id !== modifiedArray[j].sys_id && originalArray[i].type !== modifiedArray[j].type) {
      if (i ===originalArray.length - 1 && j === modifiedArray.length - 1) {
        resultingArray.push({
          sys_id: originalArray[i].sys_id,
          type: modifiedArray[j].type,
          action: 'add'
        });
      } else if (j === modifiedArray.length - 1) {
        resultingArray.push({
          sys_id: originalArray[i].sys_id,
          type: originalArray[i].type,
          action: 'remove'
        });
      }
    }
  }
}

console.log(resultingArray);

Demo

wentjun
  • 40,384
  • 10
  • 95
  • 107
1

You can convert by arrays to dictionaries. Then iterate the original and check against the modified dictionary to find remove/edit/same, and iterate the modified vs. the original dictionary to find add items. Concat the results of both iterations to get the results:

var originalArray = [{"sys_id":1234,"type":"XYZZ"},{"sys_id":1235,"type":"ABCD"},{"sys_id":1236,"type":"IJKL"},{"sys_id":1237,"type":"WXYZ"},{"sys_id":1238,"type":"LMNO"}];

var modifiedArray = [{"sys_id":1234,"type":"XYZZ"},{"sys_id":1235,"type":"ZZAA"},{"sys_id":1236,"type":"ZZZZ"},{"sys_id":1252,"type":"AAAA"}];

// create a dictionary of objects by the sys_id
function bySysId(arr) {
  return arr.reduce(function(r, o) {
    r[o.sys_id] = o;
    
    return r;
  }, {});
}

// add an action to an object
function addAction(o, action) {
  var c = {
    sys_id: o.sys_id,
    type: o.type,
    action: action
  };
  
  return c;
}

function diffArrays(original, modified) {
  var origById = bySysId(original); // create a dictionary of original by sys_id
  var modById = bySysId(modified); // create a dictionary of modified by sys_id
  
  // iterate original and action
  var modifiedOrSame = original.map(function(o) {
    var mod = modById[o.sys_id];
    
    if(!mod) return addAction(o, 'remove'); // doesn't exist in modById
    else if(mod && mod.type !== o.type) return addAction(mod, 'edit'); // exists in modified but type is different
    
    return addAction(o, 'same'); // haven't changed
  });
  
  var added = modified
    .filter(function(o) { // remove items that are in original
      return !(o.sys_id in origById);
    })
    .map(function(o) { // add the 'add' action to the items
      return addAction(o, 'add');
    });
    
  return modifiedOrSame.concat(added);
}

var result = diffArrays(originalArray, modifiedArray);

console.log(result);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

Perhaps, this is the most simplest solution and it completely supports ES5!
At first, push all items of originalArray into resultingArray and include property
action = 'remove'
Also, keep the index of the element of resultingArray into hashable object where key = sys_id

After that iterate through modifiedArray elements, and check in the hash if sys_id is already existing in the resultingArray. If it already exists, compare type. Otherwise, insert a new element where action = 'add'

I think my code is easier to understand than the above description. Here it goes:

var originalArray = [{ sys_id: 1234, type: 'XYZZ' },{ sys_id: 1235, type: 'ABCD' },{ sys_id: 1236, type: 'IJKL' },{ sys_id: 1237, type: 'WXYZ' },{ sys_id: 1238, type: 'LMNO' }];

var modifiedArray = [{ sys_id: 1234, type: 'XYZZ' },{ sys_id: 1235, type: 'ZZAA' },{ sys_id: 1236, type: 'ZZZZ' },{ sys_id: 1252, type: 'AAAA' }];

var resultingArray = [],
hash = {},
index = 1;

originalArray.forEach(function(elem) {
    var item = JSON.parse(JSON.stringify(elem));
    item['action'] = 'remove';
    resultingArray.push(item);
    hash[item.sys_id] = index++;
});

modifiedArray.forEach(function(elem) {
    index = hash[elem.sys_id];
    if(index){
        var item = resultingArray[index - 1];
        item.action = (item.type === elem.type) ? 'same' : 'edit';
        return;
    }
    var item = JSON.parse(JSON.stringify(elem));
    item['action'] = 'add';
    resultingArray.push(item);
});

console.log(resultingArray);
.as-console-wrapper {max-height:100% !important; top:0px;}

Note: In this solution, it is assumed that all items of originalArray is unique and same goes for modifiedArray.

Adnan Sharif
  • 919
  • 7
  • 17