0

I want to compare 2 arrays of objects(products) and save all changes to the changes-array.

I'm looking for an answer with a loop so that I can reuse this code even if I have 20 keys inside products and newProducts.

Products

products: [
  { id: "A32S", title: "Car" },
  { id: "D12E", title: "Rabbit" },
  { id: "A32S", title: "Ghost" },
]

New Products

newProducts: [
  { id: "A32S", title: "Ferrari" }, // <--- Change title "Car -> Ferrari"
  { id: "D12E", title: "Rabbit" },
  { id: "A32S", title: "Ghost" }
]

Changes (wished output)

changes: [
  { id: "A32S", title: "Ferrari" }
]
  • 4
    Does [this](https://stackoverflow.com/questions/21987909/how-to-get-the-difference-between-two-arrays-of-objects-in-javascript) answer your question? – Newsha Nik Dec 01 '20 at 10:55
  • Does this answer your question? [How to get the difference between two arrays of objects in JavaScript](https://stackoverflow.com/questions/21987909/how-to-get-the-difference-between-two-arrays-of-objects-in-javascript) – James Whiteley Dec 01 '20 at 11:10
  • Both yes and no. I'm looking for a loop that goes thru all keys and compares the differences. I would like to reuse this code even if I have objects with 20 keys. I definitely should have mentioned it in my question. – Michal Kurowski Dec 01 '20 at 11:24

2 Answers2

2

There is a small issue with your data, Ghost and Car have the same ID. When this is fixed, you can use the following code to create the result array:

const products = [{
    id: "A32S",
    title: "Car"
  },
  {
    id: "D12E",
    title: "Rabbit"
  },
  {
    id: "A33S",
    title: "Ghost"
  },
  {
    id: "34SC",
    title: "Apple"
  },
];

const newProducts = [{
    id: "A32S",
    title: "Ferrari"
  },
  {
    id: "D12E",
    title: "Rabbit"
  },
  {
    id: "A33S",
    title: "Ghost"
  }
]

const changes = [];

function hasSameValue(product, otherProduct, key) {
  return product[key] === otherProduct[key];
}

function exists(product, productArray) {
  for (const existingProduct of productArray) {
    if (hasSameValue(product, existingProduct, "id")) return true;
  }
  return false;
}

function getExistingProduct(id, productArray) {
  for (const product of productArray) {
    if (product.id === id) return product;
  }
}

for (const product of newProducts) {
  if (exists(product, products)) {
    const existingProduct = getExistingProduct(product.id, products);
    if (!hasSameValue(product, existingProduct, "title")) {
      changes.push(product);
    }
  }
}

for (const product of products) {
  if (!exists(product, newProducts)) {
    changes.push({
      id: product.id,
      removed: true
    });
  }
}

console.log(changes);
Titulum
  • 9,928
  • 11
  • 41
  • 79
  • Is there any way to loop thru all keys instead of making separate functions? If I have an object with 20 keys I don't want to make 20 separate functions to compare all values. – Michal Kurowski Dec 01 '20 at 11:20
  • Yes that should be possible, but could you create a new question for that as the solution will be completely different? When creating the new question, can you make sure to add the correct data (with all the keys) so that we can use this in our answer? – Titulum Dec 01 '20 at 12:04
1

You can also try some like this if you want to get all the changes (added, removed, updated and unchanged) along with checking with multiples keys for an object.


Time complexity is O(N) and Space complexity is O(N). The assumption is "id" is unique.

function getProductChanges(oldProductsList, newProductsList) {
  let oldProductsMap = {};
  let newProductsMap = {};

  let addedProductsList = [];
  let updatedProductsList = [];
  let removedProductsList = [];
  let unchangedProductsList = [];

  // considering id is unique

  // creating map for old products list
  oldProductsList.map((oldProduct) => {
    oldProductsMap[oldProduct.id] = oldProduct;
  });

  // creating map for new products list and
  // filtering newly added, updated and unchanged products list
  newProductsList.map((newProduct) => {
    newProductsMap[newProduct.id] = newProduct;

    let oldProduct = oldProductsMap[newProduct.id];

    if (oldProduct) {
      // if an existing product is updated
      if (JSON.stringify(oldProduct) !== JSON.stringify(newProduct)) {
        updatedProductsList.push(newProduct);
      } else {
        unchangedProductsList.push(newProduct);
      }
    } 
    else {
      // if a new product is added
      addedProductsList.push(newProduct);
    }  
  });

  // populating removedProductsList
  removedProductsList = oldProductsList.filter((oldProduct) => !newProductsMap[oldProduct.id]);

  return {
    added: addedProductsList,
    updated: updatedProductsList,
    removed: removedProductsList,
    unchanged: unchangedProductsList,
  };
}

Input:

let products = [
  { id: "A32S", title: "Car" },
  { id: "D12E", title: "Rabbit" },
  { id: "A321", title: "Ghost" },
  { id: "A320", title: "Lion" },
];

let newProducts = [
  { id: "A32S", title: "Ferrari" },
  { id: "D12E", title: "Rabbit" },
  { id: "A321", title: "Ghost" },
  { id: "A322", title: "Zebra" },
];

console.log(getProductChanges(products, newProducts));

Output:

{
  added: [{ id: "A322", title: "Zebra" }],
  removed: [{ id: "A320", title: "Lion" }],
  unchanged: [
    { id: "D12E", title: "Rabbit" },
    { id: "A321", title: "Ghost" },
  ],
  updated: [{ id: "A32S", title: "Ferrari" }],
}
Souvik Nandi
  • 354
  • 2
  • 6