Diffing 2 arrays, i.e which items to add and remove - javascript

Given:
array of "current" objects [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}]
array of "new" objects [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}]
How to determine which objects
to add (not in "current") and
to remove (not in "new")?
In this case:
{id: '2'},{id: '3'},{id: '4'} should be removed
{id: '6'},{id: '7'},{id: '8'} should be added
Performance is not extremely important, my dataset is usually around 200.
Edit: I need to know about elements to be added/removed because "current" array correlates to DOM nodes and I don't just want to delete them all and add from scratch - already tried that and performance is far from perfect.

In both cases, this is a classic use case of the set operation "difference". All you need to do is define a difference function, and then apply it with current.difference(new) and new.difference(current).
function difference(a, b, compare) {
let diff = [];
for (let ai = 0; ai < a.length; ai++) {
let exists = false;
for (let bi = 0; bi < b.length; bi++) {
if (compare(a[ai], b[bi])) {
exists = true;
break;
}
}
if (!exists) diff.push(a[ai]);
}
return diff;
}
function getRemoved(oldA, newA) {
return difference(oldA, newA, (a, b) => a.id == b.id);
}
function getAdded(oldA, newA) {
return difference(newA, oldA, (a, b) => a.id == b.id);
}
let current = [{id: '1'}, {id: '2'}, {id: '3'}, {id: '4'}, {id: '5'}];
let newArr = [{id: '1'}, {id: '5'}, {id: '6'}, {id: '7'}, {id: '8'}];
console.log(getRemoved(current, newArr));
console.log(getAdded(current, newArr));

For each item in current that is not found in new. That item is deleted.
For each item in new that is not in current. That item is added.
You run the run these checks in two different loops one after the other.

You could go with something like this :
const currentElements = [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}]
const newElements = [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}]
const elementsToAdd = newElements.filter(e1 => !currentElements.find(e2 => e2.id === e1.id))
const elementsToRemove = currentElements.filter(e1 => !newElements.find(e2 => e2.id === e1.id))
console.log({elementsToAdd, elementsToRemove})
Basically, I take an array, and find the elements not contained in the other array.
For the elements to add, check the elements in newElements that are not in currentElements, and vice versa.

You could use Set data structure to calculate the different and filter the expeced values (using its has method). Time complexity for this approach will be linear (O(n))
Set use hash table so complexity for retrival/lookup is O(1) (doc, Theory > Lookup Speed)
const prev = [{ id: "1" }, { id: "2" }, { id: "3" }, { id: "4" }, { id: "5" }] // length n
const current = [
{ id: "1" },
{ id: "5" },
{ id: "6" },
{ id: "7" },
{ id: "8" },
] // length m
const prevIdSet = new Set(prev.map((o) => o.id)) // O(n)
const currentIdSet = new Set(current.map((o) => o.id)) // O(m)
function difference(setA, setB) {
let _difference = new Set(setA)
for (let elem of setB) {
_difference.delete(elem)
}
return _difference
}
const removedIdSet = difference(prevIdSet, currentIdSet) // O(m)
const addedIdSet = difference(currentIdSet, prevIdSet) // O(n)
const removed = prev.filter((o) => removedIdSet.has(o.id)) // O(n)
const added = current.filter((o) => addedIdSet.has(o.id)) // O(m)
console.log("removed", removed)
console.log("added", added)
// Total complexity O(constantA * n + constantB * m) ~ O(n + m)

You could use filter. Something like this for items to remove:
arr1.filter(function(i) {return arr2.indexOf(i) < 0;});
And swap the arrays for items to add. For example:
let arr1 = [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}];
let arr2 = [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}];
arr1 = arr1.map((obj) => obj.id);
arr2 = arr2.map((obj) => obj.id);
console.log("Remove these IDs: ", arr1.filter(function(i) {return arr2.indexOf(i) < 0;}));
console.log("Add these IDs: ", arr2.filter(function(i) {return arr1.indexOf(i) < 0;}));

You you only needs the ids: You could extract the ids with flatMap and than filter with every for not incudes.
let old = [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}];
let act = [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}];
let oldIds = old.flatMap(el => el.id);
let actIds = act.flatMap(el => el.id);
let add = actIds.filter(id => oldIds.every(old=> !old.includes(id)));
let del = oldIds.filter(id => actIds.every(act=> !act.includes(id)));
console.log('Add: ', add);
console.log('Del: ', del);

Related

Array compare with for each performance issue

I have two arrays with array of objects as follows and one array will have more than 10k records and other have below 100 records
let bigArray = [{id:1, name:"Raj", level:0}, {id:2, name:"sushama", level:2}, {id:3, name:"Sushant", level:0}, {id:4, name:"Bhaskar", level:2},....upto 30k records]
let smallArray = [{id:2, name:"sushama"}, {id:3, name:"Sushant"}....upto 100 records]
I want to find where in the index of bigArray in which the object from smallArray resides and add to another array say indexArray I tried below
let indexArray = [];
bigArray.forEach((element, i) => {
smallArray.forEach(ele => {
if (element.name == ele.name && element.id == ele.id) {
indexArray.push(i); return;
}
});
});
But it takes time. What would be the fastest approach?
You can turn your O(N^2) approach into an O(N) approach by reducing the bigArray into an object indexed by a key made up from the name and id. Join the name and id by a character that isn't contained in either, such as _:
const indexArray = [];
const bigArrayIndiciesByNameAndId = bigArray.reduce((a, { name, id }, i) => {
a[name + '_' + id] = i;
return a;
}, {});
smallArray.forEach(ele => {
const keyToFind = ele.name + '_' + ele.id;
const foundIndex = bigArrayIndiciesByNameAndId[keyToFind];
if (foundIndex) {
indexArray.push(foundIndex);
}
});
You could take a Map and map the found indices.
const getKey = ({ id, name }) => [id, name].join('|');
let bigArray = [{ id: 1, name: "Raj", level: 0 }, { id: 2, name: "sushama", level: 2 }, { id: 3, name: "Sushant", level: 0 }, { id: 4, name: "Bhaskar", level: 2 }],
smallArray = [{ id: 2, name: "sushama" }, { id: 3, name: "Sushant" }],
map = new Map(bigArray.map((o, i) => [getKey(o), i]))
indexArray = smallArray.map((o) => map.get(getKey(o)));
console.log(indexArray);
return will not "break" the forEach loop. A forEach can't be stopped. The forEach callback function will be called one time per items always. When you find the element, continue running the forEach loop is a waste of resouces.
You should use for instead:
let indexArray = [];
bigArray.forEach((element, i) => {
for (var ii = 0; ii < smallArray.length; ii++) {
var ele = smallArray[ii];
if (element.name == ele.name && element.id == ele.id) {
indexArray.push(i);
break; // This will break the "for" loop as we found the item
}
}
});
TIP: Always have a good indentation in your code. Actually your code is really bad indented to identify code blocks at first sight. I fixed it in this example.

How to check an array of objects is totally belongs to another?

There are two array of objects like this
var a = [
{id:'1'},
{id:'2'}
];
var b = [
{id:'1',name:'a'},
{id:'2',name:'b'},
{id:'3',name:'c'}
]
And I need a function, if all ids of the elements of array a can be found in array b, it will return true, otherwise return false
You could use a Set and check with Array#every.
const check = (a, b) => a.every((s => ({ id }) => s.has(id))(new Set(b.map(({ id }) => id))));
var a = [{ id: '1' }, { id: '2' }],
b = [{ id: '1', name: 'a' }, { id: '2', name: 'b' }, { id: '3', name: 'c' }];
console.log(check(a, b));
This is not the most efficient way, as it needs to create the list of ids in b for each item in a.
var a = [
{id:'1'},
{id:'2'},
{id:'7'},
];
var b = [
{id:'1',name:'a'},
{id:'2',name:'b'},
{id:'3',name:'c'}
]
const allPresent = a
.map(item => item.id)
.map(id => Object.assign({
id,
present: b
.map(item => item.id)
.indexOf(id) > -1,
}))
console.log(allPresent)
You can use the following
var a = [
{id:'1'},
{id:'2'}
];
var b = [
{id:'1',name:'a'},
{id:'2',name:'b'},
{id:'3',name:'c'}
]
console.log(checkobject());
function checkobject()
{
var checkid=true;
a.forEach(function(el)
{
var check=b.findIndex(function(element) {
return element.id===el.id;
});
if(check==-1)
{
checkid=false;
return;
}
});
return checkid;
}
Can use this simple way:
var a = [
{id:'1'},
{id:'2'}
];
var b = [
{id:'1',name:'a'},
{id:'2',name:'b'},
{id:'3',name:'c'}
];
var id_a = a.map((current)=>{
return current.id;
}); console.log(id_a); // ["1", "2"]
var id_b = b.map((current)=>{
return current.id;
}); console.log(id_b); // ["1", "2", "3"]
// check if id_a in id_b, check total element of each set
let bool = Array.from(new Set(id_b) ).length == Array.from(new Set(id_b.concat(id_a)) ).length;
console.log(bool);
Solution using Array.prototype.filter and Array.prototype.some:
const includeCheck = (a, b) => {
if (b.filter(el => a.some(obj => obj.id === el.id)).length === b.length) {
console.log('b fully includes a')
} else {
console.log('b does not fully include a')
}
}
let a = [{id:'1'}, {id:'2'}, {id:'3'}],
b = [{id:'1',name:'a'}, {id:'2',name:'b'}, {id:'3',name:'c'}]
includeCheck(a, b);
It compares lengths of original b array and b array filtered by a array's ids to determine whether b has all a ids or not.

Trying to avoid duplicates when creating new array from comparing value of two others

I have an app where I need to create a new array by pushing values from two other arrays after comparing what values in one array exist in another.
Example:
From these two arrays...
sel[1,4];
bus[1,2,3,4,5,6];
The desired result is a new object array which will populate a repeater of checkboxes in my view...
newList[{1:true},{2:false},{3:false},{4:true},{5:false},{6:false}];
The problem I'm running into, is that my code is creating duplicates and I'm not seeing why.
Here is my code:
var newList = [];
var bus = self.businesses;
var sel = self.campaign.data.businesses;
for( var b = 0; b < bus.length; b++ ){
if(sel.length > -1){
for( var s = 0; s < sel.length; s++){
if( bus[b]._id === sel[s].business_id){
newList.push({'business_id':bus[b]._id, 'name':bus[b].business_name, 'selected':true});
} else {
newList.push({'business_id':bus[b]._id, 'name':bus[b].business_name, 'selected':false});
}
}
} else {
console.log('hit else statement');
newList.push({'business_id':bus[b]._id, 'name':bus[b].business_name, 'selected':false});
}
}
I need fresh eyes on this as it looks correct to me... but obviously I'm missing something. :-)
Your code produces duplicates because you push selected: false objects into your newList every time the inner loop is run and the ids don't match:
for( var s = 0; s < sel.length; s++){
if( bus[b]._id === sel[s].business_id){
newList.push({'business_id':bus[b]._id, 'name':bus[b].business_name, 'selected':true});
} else {
// THIS LINE CAUSES THE DUPLICATES:
newList.push({'business_id':bus[b]._id, 'name':bus[b].business_name, 'selected':false});
}
}
To fix your code, move this line out of the inner loop into the outer loop below and add a continue outer; to the inner loop's if body. Then you need to place the outer label directly in front of the outer loop: outer: for( var b = 0; b < bus.length; b++ ) ....
However, I recommend a simpler implementation as follows:
let selection = [{_id: 1, business_name: 'A'}];
let businesses = [{_id: 1, business_name: 'A'}, {_id: 2, business_name: 'B'}];
let result = businesses.map(business => ({
'business_id': business._id,
'name': business.business_name,
'selected': selection.some(selected => business._id == selected._id)
}));
console.log(result);
Appendix: Same implementation with traditional functions:
var selection = [{_id: 1, business_name: 'A'}];
var businesses = [{_id: 1, business_name: 'A'}, {_id: 2, business_name: 'B'}];
var result = businesses.map(function(business) {
return {
'business_id': business._id,
'name': business.business_name,
'selected': selection.some(function(selected) { return business._id == selected._id })
};
});
console.log(result);
I suggest to use a different approach by using an object for sel and the just iterate bus for the new array with the values.
function getArray(items, selected) {
var hash = Object.create(null);
selected.forEach(function (a) {
hash[a] = true;
});
return items.map(function (a) {
var temp = {};
temp[a] = hash[a] || false;
return temp;
});
}
console.log(getArray([1, 2, 3, 4, 5, 6], [1, 4]));
ES6 with Set
function getArray(items, selected) {
return items.map((s => a => ({ [a]: s.has(a) }))(new Set(selected)));
}
console.log(getArray([1, 2, 3, 4, 5, 6], [1, 4]));
You can use map() method on bus array and check if current value exists in sel array using includes().
var sel = [1,4];
var bus = [1,2,3,4,5,6];
var result = bus.map(e => ({[e] : sel.includes(e)}))
console.log(result)
This combines both Nina Scholz elegant ES6 approach with le_m's more specific solution to give you something that is shorter, versatile, and repurposable.
function getArray(items, selected, [...id] = selected.map(selector => selector._id)) {
return [items.map((s => a => ({
[a._id + a.business_name]: s.has(a._id)
}))(new Set(id)))];
}
console.log(...getArray([{
_id: 1,
business_name: 'A'
}, {
_id: 2,
business_name: 'B'
}, {
_id: 3,
business_name: 'C'
}, {
_id: 4,
business_name: 'D'
}, {
_id: 5,
business_name: 'E'
}, {
_id: 6,
business_name: 'F'
}], [{
_id: 1,
business_name: 'A'
}, {
_id: 2,
business_name: 'B'
}]));

How to find difference in two not distinct arrays in javascript? I used underscore but I am open to vanilla JS

If I have two arrays like
array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}]
array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}]
The expected result should be [{id:'id3'}]
My method works perfectly for distinct arrays.
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
var diff = _.difference(_.union(idsFrom1, idsFrom2), _.intersection(idsFrom1, idsFrom2))
I think your issue is that you have to use some mechanism to count the deleted elements, such as _.countBy:
var first = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}, {
id: 'id3'
}]
var second = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}]
function unwrap (o) {
return o[this]
}
function wrap (k) {
return _.object([k], [String(this)])
}
function counted(array) {
return _.countBy(array, unwrap, 'id')
}
function constant() {
return String(this)
}
function difference(first, second) {
return _.flatten(_.values(_.mapObject(counted(first), function(v, k) {
return _.range(0, v - this[k]).map(constant, k)
}, counted(second)))).map(wrap, 'id')
}
console.log(difference(first, second))
<script src="http://underscorejs.org/underscore-min.js"></script>
This one doesn't use splice() and instead it "marks" each array member that should be removed by setting it to null and then uses compact() to take them all out at once. The speed of the indexOf() actually improves as idsFrom2 gets smaller with splice, so the faster approach depends on the number of matches and sizes of arrays.
array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}]
array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}]
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
for(var i=0;i<idsFrom1.length;i++) {
var index = idsFrom2.indexOf(idsFrom1[i]);
if(index > -1) {
idsFrom1[i] = null;
idsFrom2[index] = null;
}
}
diff = [].concat(_.compact(idsFrom1),_.compact(idsFrom2));
objarr = [];
for(var i=0;i<diff.length;i++) objarr.push({"id":diff[i]});
console.log(objarr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
This approach steps through one of the arrays and removes elements from both arrays that match as it moves through the array. That way anything without a partner in both arrays will remain at the end:
var array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}]
var array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}]
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
for(var i=0;i<idsFrom1.length;) {
var index = idsFrom2.indexOf(idsFrom1[i]);
if(index > -1) {
idsFrom1.splice(i,1);
idsFrom2.splice(index,1);
}
else i++;
}
diff = [].concat(idsFrom1,idsFrom2);
objarr = [];
for(var i=0;i<diff.length;i++) objarr.push({"id":diff[i]});
console.log(objarr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Ok, this one appears to be the fastest in all of my tests. Use the splice() on the idsFrom2 array to get it to shrink as you move through idsFrom1, and set null on the idsFrom1 array matches and use compact() afterwards.
var array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}];
var array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}];
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
for(var i=0;i<idsFrom1.length;i++) {
var index = idsFrom2.indexOf(idsFrom1[i]);
if(index > -1) {
idsFrom1[i] = null;
idsFrom2.splice(index,1);
}
}
diff = [].concat(_.compact(idsFrom1),idsFrom2);
objarr = [];
for(var i=0;i<diff.length;i++) objarr.push({"id":diff[i]});
console.log(objarr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Try this. If you don't want to modify your original arrays, then you can create copies and use them for the modification
var array1 = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}, {
id: 'id3'
}],
array2 = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}];
_.each(array1, function(obj1, i) {
_.each(array2, function(obj2, j) {
// Make sure the objects are not cleared off by a previous comparison
var isEqual = array1[i] && array2[j] && _.isEqual(obj1, obj2);
if (isEqual) {
array1[i] = array2[j] = null;
}
});
});
console.log([].concat(_.compact(array1), _.compact(array2)));
<script src="http://underscorejs.org/underscore-min.js"></script>

JavaScript merging objects by id [duplicate]

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
How to merge two arrays in JavaScript and de-duplicate items
(89 answers)
Closed 7 months ago.
What's the correct way to merge two arrays in Javascript?
I've got two arrays (for example):
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
I want to be able to end up with something like:
var a3 = [{ id : 1, name : "test", count : "1"},
{ id : 2, name : "test2", count : "2"}]
Where the two arrays are being joined based on the 'id' field and extra data is simply being added.
I tried to use _.union to do this, but it simply overwrites the values from the second array into the first one
Short ES6 solution
const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))
This should do the trick:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
});
This assumes that the id of the second object in a1 should be 2 rather than "2"
Assuming IDs are strings and the order does not matter, you can
Create a hash table.
Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, update it with Object.assign (ES6, can be polyfilled).
Get an array with the values of the hash map.
var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
return hash[key];
});
In ECMAScript6, if the IDs are not necessarily strings, you can use Map:
var hash = new Map();
a1.concat(a2).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());
ES6 simplifies this:
let merge = (obj1, obj2) => ({...obj1, ...obj2});
Note that repeated keys will be merged, and the value of the second object will prevail and the repeated value of the first object will be ignored.
Example:
let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};
merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}
Complete solution (with Lodash, not Underscore)
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1"}
1: {id: 2, name: "test2", count: "2"}
If you have an array of arrays to merge you can do it like this:
var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}
reduce version.
var a3 = a1.concat(a2).reduce((acc, x) => {
acc[x.id] = Object.assign(acc[x.id] || {}, x);
return acc;
}, {});
_.values(a3);
I think it's common practice in functional language.
Already there are many great answers, I'll just add another one which is from a real problem I needed to solve yesterday.
I had an array of messages with user ids, and one array of users containing users' names and other details. This is how I managed to add user details to the messages.
var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];
var messagesWithUserNames = messages.map((msg)=> {
var haveEqualId = (user) => user.id === msg.userId
var userWithEqualId= users.find(haveEqualId)
return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)
Vanilla JS solution
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
const merge = (arr1, arr2) => {
const temp = []
arr1.forEach(x => {
arr2.forEach(y => {
if (x.id === y.id) {
temp.push({ ...x, ...y })
}
})
})
return temp
}
console.log(merge(a1, a2))
The lodash implementaiton:
var merged = _.map(a1, function(item) {
return _.assign(item, _.find(a2, ['id', item.id]));
});
The result:
[
{
"id":1,
"name":"test",
"count":"1"
},
{
"id":2,
"name":"test2",
"count":"2"
}
]
Wanted to add this answer which is derived from #daisihi answer above. Main difference is that this uses the spread operator.
Also, at the end I remove the id because it was not desirable in the first place.
const a3 = [...a1, ...a2].reduce((acc, x) => {
acc[x.id] = {...acc[x.id] || {}, ...x};
return acc;
}, {});
This part was taken from another post. removing a property from a list of objects in an array
const newArray = Object.values(a3).map(({id, ...keepAttrs}) => keepAttrs);
Found other solutions failing for some cases, so writing a better one here
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 3, count : "3"}, { id : 1, count : "1"}, {id : 2, count : "2"}]
const mergeHelper = new Map(a1.map(x => [x.id, x]));
for (const x of a2) {
if (mergeHelper.has(x.id)) {
const item = mergeHelper.get(x.id);
mergeHelper.set(x.id, {...item, ...x});
} else {
mergeHelper.set(x.id, x);
}
}
const mergedList = [...mergeHelper.values()];
// For sorted array
// const mergedSortedList = [...mergeHelper.values()].sort((a, b) => a.id - b.id);
console.log(mergedList)
Using js Map is way faster than other approaches, helps when array length is huge.
A working TypeScript version:
export default class Merge {
static byKey(a1: any[], a2: any[], key: string) {
const res = a1.concat(a2).reduce((acc, x) => {
acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
return acc;
}, {});
return Object.entries(res).map(pair => {
const [, value] = pair;
return value;
});
}
}
test("Merge", async () => {
const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
const a2 = [{ id: "2", value: "3" }];
expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
{
id: "1",
value: "1"
},
{ id: "2", value: "3" }
]);
});
try this
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
let arr3 = a1.map((item, i) => Object.assign({}, item, a2[i]));
console.log(arr3);
How about this?
const mergeArrayObjects = (arr1: any[], arr2: any[], mergeByKey: string): any[] => {
const updatedArr = [];
for (const obj of arr1) {
const arr1ValueInArr2 = arr2.find(
a => a[mergeByKey] === obj[mergeByKey],
);
if (arr1ValueInArr2) {
updatedArr.push(Object.assign(obj, arr1ValueInArr2));
} else {
updatedArr.push(obj);
}
}
const mergeByKeyValuesInArr1 = arr1.map(a => a[mergeByKey]);
const remainingObjInArr2 = arr2.filter(a => !mergeByKeyValuesInArr1.includes(a[mergeByKey]) )
return updatedArr.concat(remainingObjInArr2)
}
You can write a simple object merging function like this
function mergeObject(cake, icing) {
var icedCake = {}, ingredient;
for (ingredient in cake)
icedCake[ingredient] = cake[ingredient];
for (ingredient in icing)
icedCake[ingredient] = icing[ingredient];
return icedCake;
}
Next, you need to do use a double-loop to apply it to your data structre
var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i) // for each item in a2
for (j = 0; i < a3.length; ++i) // look at items in other array
if (a2[i]['id'] === a3[j]['id']) // if matching id
a3[j] = mergeObject(a3[j], a2[i]); // merge
You can also use mergeObject as a simple clone, too, by passing one parameter as an empty object.
const a3 = a1.map(it1 => {
it1.test = a2.find(it2 => it2.id === it1.id).test
return it1
})
If you have exactly the same number of items in both array with same ids you could do something like this.
const mergedArr = arr1.map((item, i) => {
if (item.ID === arr2[i].ID) {
return Object.assign({}, item, arr2[i]);
}
});
function mergeDiffs(Schedulearray1, Schedulearray2) {
var secondArrayIDs = Schedulearray2.map(x=> x.scheduleid);
return Schedulearray1.filter(x=> !secondArrayIDs.includes(x.scheduleid)).concat(Schedulearray2);
}
None of them worked for me. I wrote own:
const formatteddata=data.reduce((a1,a2)=>{
for (let t=0; t<a1.length; t++)
{var id1=a1[t].id
for (let tt=0; tt<a2.length; tt++)
{var id2=a2[tt].id
if(id1==date2)
{a1[t]={...a1[t],...a2[tt]}}
}
}
return a1
})
works with any amount of arrays of objects in arrays, with varying length and not always coinsciding dates

Categories

Resources