Home > database >  Comparing two arrays of objects for matching properties then creating two new arrays
Comparing two arrays of objects for matching properties then creating two new arrays

Time:09-28

I have the following two arrays:

var oldOrder = [{"Id": "10","Qty": 1}, {"Id": "3","Qty": 2}, {"Id": "9","Qty": 2}]; 
var newOrder = [{"Id": "5","Qty": 2},{"Id": "10","Qty": 3}, {"Id": "9","Qty": 1}];

I need to end up with a newArray containing only one object per Id, but if the ids in an object in the oldOrder array and newOrder array then I need to get the difference in the Qty positive or negative. So for example the above would create this new array:

var newArray = [{"Id": "5","Qty": 2},{"Id": "10","Qty": 2}, {"Id": "9","Qty": -1}];

I would also like to get the dropped orders that are present in the oldOrder Array but not in the newOrder array:

var droppedOrders = [{"Id": "3","Qty": 2}];

I have already got this logic for getting the dropped orders but I am struggling with creating the newArray and tying the logic into the same function if possible?

function comparer(otherArray){
  return function(current){
    var onlyInOld = otherArray.filter(function(other){
      return other.Id == current.Id;
    }).length == 0;
    return onlyInOld;
  }
}

var droppedOrders= oldOrder.filter(comparer(newOrder));
console.log(droppedOrders);

Edited to add that I cannot use ESC6 features like spread or fat arrow etc.

CodePudding user response:

You can easily achieve the result using Map

1) Using ES6

var oldOrder = [
  { Id: "10", Qty: 1 },
  { Id: "3", Qty: 2 },
  { Id: "9", Qty: 2 },
];
var newOrder = [
  { Id: "5", Qty: 2 },
  { Id: "10", Qty: 3 },
  { Id: "9", Qty: 1 },
];

const oldMap = new Map(oldOrder.map((o) => [o.Id, o]));
const newMap = new Map(newOrder.map((o) => [o.Id, o]));

const result = newOrder.map((o) =>
  oldMap.has(o.Id) ? { ...o, Qty: o.Qty - oldMap.get(o.Id).Qty } : o
);

const droppedOrders = oldOrder.filter(({ Id }) => !newMap.has(Id));

console.log(result);
console.log(droppedOrders);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */

.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

2) Using ES5

var oldOrder = [
  { Id: "10", Qty: 1 },
  { Id: "3", Qty: 2 },
  { Id: "9", Qty: 2 },
];
var newOrder = [
  { Id: "5", Qty: 2 },
  { Id: "10", Qty: 3 },
  { Id: "9", Qty: 1 },
];

const oldMap = {};
const newMap = {};
oldOrder.forEach(function (o) {
  oldMap[o.Id] = o;
});
newOrder.forEach(function (o) {
  newMap[o.Id] = o;
});

const result = newOrder.map(function (o) {
  return oldMap[o.Id]
    ? Object.assign({}, o, { Qty: o.Qty - oldMap[o.Id].Qty })
    : o;
});

const droppedOrders = oldOrder.filter(function (o) {
  return !newMap[o.Id];
});

console.log(result);
console.log(droppedOrders);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

One way to achieve that is by keying the old and new order arrays by Id. After keying everything else should be a simple as using filter and map:

var oldOrder = [{"Id": "10","Qty": 1}, {"Id": "3","Qty": 2}, {"Id": "9","Qty": 2}]; 
var newOrder = [{"Id": "5","Qty": 2},{"Id": "10","Qty": 3}, {"Id": "9","Qty": 1}];
    
var keyedOldOrder = keyBy(oldOrder, 'Id');
var keyedNewOrder = keyBy(newOrder, 'Id');

var newArray = newOrder.map(function (entity) {
  var subtract = keyedOldOrder.hasOwnProperty(entity.Id)
    ? keyedOldOrder[entity.Id].Qty
    : 0;
  return Object.assign({}, entity, {
    Qty: entity.Qty - subtract
  });
});

var droppedOrders = oldOrder.filter(function (entity) {
  return !keyedNewOrder.hasOwnProperty(entity.Id);
});

console.log(newArray);
console.log(droppedOrders);

function keyBy(array, field)
{
  return array.reduce(function (carry, entity) {
    carry[entity[field]] = entity;
    return carry;
  }, {});
}

CodePudding user response:

You could take a Map and perform a single loop for every array and redere the result to the wanted target arrays.

const
    addToMap = (map, target) => ({ Id, Qty }) => map.set(Id, { target, Qty: Qty - (map.get(Id)?.Qty || 0) }),
    oldOrder = [{ Id: "10", Qty: 1 }, { Id: "3", Qty: 2 }, { Id: "9", Qty: 2 }],
    newOrder = [{ Id: "5", Qty: 2 }, { Id: "10", Qty: 3 }, { Id: "9", Qty: 1 }],
    map = new Map,
    orders = [],
    dropped = [];

oldOrder.forEach(addToMap(map, dropped));
newOrder.forEach(addToMap(map, orders));

map.forEach(({ target, Qty }, Id) => target.push({ Id, Qty }));

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

CodePudding user response:

Try using Array.map and Array.filter

var oldOrder = [{ "Id": "10", "Qty": 1 }, { "Id": "3", "Qty": 2 }, { "Id": "9", "Qty": 2 }];
var newOrder = [{ "Id": "5", "Qty": 2 }, { "Id": "10", "Qty": 3 }, { "Id": "9", "Qty": 1 }];

const newArray = newOrder.map((node) => {
  const nodeFromOldArray = oldOrder.find((item) => item.Id === node.Id);
  if (nodeFromOldArray) {
    return {
      Id: node.Id,
      Qty: nodeFromOldArray.Qty - node.Qty
    }
  } else {
    return node;
  }
});

const droppedOrders = oldOrder.filter((item) => !newArray.find(node => node.Id === item.Id))

console.log(newArray);
console.log(droppedOrders);

  • Related