Compare two arrays and update them with new values, preserving existing objects with javascript

Below are my two arrays. I want to compare them, and the resulting array should contain updated values. Arrays span n levels, i.e. There are no fixed levels.

The first array, i.e. array before update.

var parentArray1=[ { "id": 1, "name": "test", "context": [ { "id": 1.1, "name": "test 1.1" } ] }, { "id": 2, "name": "test" }, { "id": 3, "name": "test", "context": [ { "id": 3.1, "name": "test 3.1" } ] }, { "id": 4, "name": "test" } ] 

The operations that I performed are

1. Adding a new item 2. Updating an existing item

As a result of these two operations, I get the changed values ​​in another array. i.e.

 var changedArray= [ { "id": 1, "name": "test1", "context": [ { "id": 1.1, "name": "Changed test 1.1" } ] }, { "id": 5, "name": "test5" } ] 

Now I have written a generic function that goes through parentArray1 and uses unique properties. I need to either add a new item if the item is in a modified Array , or update an existing item at any level

The resulting array should be ...

 [ { "id": 1, "name": "test", "context": [ { "id": 1.1, "name": "Changed test 1.1" } ] }, { "id": 2, "name": "test" }, { "id": 3, "name": "test", "context": [ { "id": 3.1, "name": "test 3.1" } ] }, { "id": 4, "name": "test" }, { "id": 5, "name": "test5" } ] 

General function:

 compareArray(parentArray1, changedArray, ["id"]); function compareArray(array1, array2, propertyArray) { var newItem = new Array(); array2.map(function(a1Item) { array1.map(function(a2Item) { / If array loop again / if (a2Item.constructor === Array) { compareArray(a2Item, a1Item) } else { / loop the property name to validate / propertyArray.map(function(property) { if (a2Item[property]) { if (a2Item[property] === a1Item[property]) { a2Item = a1Item } else { var isAvailable = _.find(newItem, function(item) { return item[property] === a1Item[property] }) if (!isAvailable) { newItem.push(a1Item); } } } }) } }); }); / Insert the new item into the source array / newItem.map(function(item) { array1.push(item); }); console.log("After Compare : " + array1); } 
+5
source share
3 answers

Here is what I came up with:

 function sameKeys(o1, o2, keys) { for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (!o1.hasOwnProperty(key) || !o2.hasOwnProperty(key)) throw 'compared objects do not have the key ' + key; if (o1[key] !== o2[key]) return false; } return true; } function isNothing(o) { return typeof(o) === 'undefined' || o === null; } // this does not work if objects have functions as properties function clone(o) { if (isNothing(o)) return o; return JSON.parse(JSON.stringify(o)); } function extend(o1, o2, keys) { if (isNothing(o2)) return; if (isNothing(o1)) throw ('first parameter cannot be empty'); if (typeof(o1) != 'object' || typeof(o2) != 'object') throw ('extend only works on objects'); Object.keys(o2).forEach(function (key) { var newVal = o2[key]; if (o1.hasOwnProperty(key)) { if (isNothing(newVal)) { delete o1[key]; } else if (Array.isArray(newVal)) { compareArray(o1[key], newVal, keys); } else { switch (typeof(newVal)) { case 'object': extend(o1[key], newVal, keys); break; case 'boolean': case 'number': case 'string': o1[key] = newVal; break; default: throw 'not supported property type: ' + typeof(newVal); } } } else { o1[key] = clone(newVal); } }); } function removeFromArray(arr, ids, keyArray) { var indexes = []; var it1s = arr.forEach(function (it, idx) { if (sameKeys(ids, it, keyArray)) { indexes.push(idx); } else { Object.keys(it).forEach(function (key) { var newVal = it[key]; if (Array.isArray(newVal)) { removeFromArray(it[key], ids, keyArray); } }); } }); if (indexes.length) { if (indexes.length > 1) throw 'found multiple possible objects for the same key combination' arr.splice(indexes[0], 1); } } function compareArray(a1, a2, keyArray) { a2.forEach(function (it2) { var it1s = a1.filter(function (it) { return sameKeys(it2, it, keyArray); }); var it1; if (!it1s.length) { it1 = clone(it2); a1.push(it1); } else { if (it1s.length > 1) throw 'found multiple possible objects for the same key combination' it1 = it1s[0]; extend(it1, it2, keyArray); } if (it2.removedIds) { it2.removedIds.forEach(function (ids) { removeFromArray(a1, ids, keyArray); }); } }); } 

Use it with compareArray(parentArray1,changedArray,['id']);

Note that this will not work with objects that contain functions. In addition, if the arrays are large, perhaps the best solution would be to sort both arrays by key, and then always look up from the last object found. That is all I got now.

Updated with some concepts from Nina and some code cleanup.

As I understand it, you only want to add properties. So, extend ({a: {b: 2}}, {a: {c: 3}}) will result in {a: {b: 2, c: 3}}. If this is not what you wanted, let me know.

I also added functionality to remove identifiers. If any of the objects in the array contains an removedIds array of the form [{id: 4},{id: 5}] , then elements with these identifiers will be deleted from the original array.

0
source

I suggest using a temporary object to refer to id and update if exists or click if not.

 var parentArray1 = [{ "id": 1, "name": "test", "context": [{ "id": 1.1, "name": "test 1.1" }] }, { "id": 2, "name": "test" }, { "id": 3, "name": "test", "context": [{ "id": 3.1, "name": "test 3.1" }] }, { "id": 4, "name": "test" }], changedArray = [{ "id": 1, "name": "test1", "context": [{ "id": 1.1, "name": "Changed test 1.1" }] }, { "id": 5, "name": "test5" }]; function insert(array, data) { function iter(array) { array.forEach(function (a) { if (!('id' in a)) { return; } if (o[a.id] !== a) { o[a.id] = a; } Object.keys(a).forEach(function (k) { Array.isArray(a[k]) && iter(a[k]); }); }); } var o = {}; iter(array); data.forEach(function (a) { if (o[a.id]) { Object.keys(a).forEach(function (k) { o[a.id][k] = a[k]; }); return; } array.push(a); }); } insert(parentArray1, changedArray); document.write('<pre>' + JSON.stringify(parentArray1, 0, 4) + '</pre>'); 
+1
source

Slight code modification to suit your conditions. Give it a try!

 function compareArray(originalArray, destinationArray, propertyArray) { var newItem = new Array(), processedItem = new Array(); for (var i = 0; i < originalArray.length; i++) { var sourceElement = originalArray[i]; for (var j = 0; j < destinationArray.length; j++) { var destinationElement = destinationArray[j]; var isUpdated = false; if (sourceElement.constructor === Array) { compareArray(sourceElement, destinationElement, propertyArray); } else { /* loop the property name to validate */ propertyArray.map(function(property) { if (sourceElement[property]) { if (sourceElement[property] === destinationElement[property]) { originalArray[i] = _.clone(destinationElement); isUpdated = true; return; } else { var isAvailable = _.find(newItem, function(item) { return item[property] === destinationElement[property]; }); if (!isAvailable) { var isAlreadyProcessed = _.find(processedItem, function(item) { return item[property] === destinationElement[property]; }); if(!isAlreadyProcessed){ newItem.push(destinationElement); } } } } }); } if (isUpdated === true) { break; } } processedItem.push(sourceElement); } newItem.map(function(item) { originalArray.push(item); }); return originalArray; } 
+1
source

Source: https://habr.com/ru/post/1245473/


All Articles