Search and update array based on key - javascript

I have two array, need to update the second array by searching the position in the first array.
let arr1 = [{"LEVEL":4,"POSITION":"RGM"},{"LEVEL":5,"POSITION":"GM"},{"LEVEL":5,"POSITION":"GMH"}]
let arr2 = [{"EMAIL":"test1#stc.com","POSITION":"GM"},
{"EMAIL":"test2#stc.com","POSITION":"GMH"},
{"EMAIL":"test3#stc.com","POSITION":"RGM"},
{"EMAIL":"test3#CSR.COM.AU","POSITION":"GM"}]
Output Array
output = [ {"LEVEL":5,"EMAIL":"test1#stc.com","POSITION":"GM"},
{"LEVEL":5,"EMAIL":"test2#stc.com",""POSITION":"GMH"},
{"LEVEL":4,"EMAIL":"test3#stc.com","POSITION":"RGM"},
{"LEVEL":5,"EMAIL":"test3#CSR.COM.AU","POSITION":"GM"}]
I tried using the below code to filter but gives empty array, so not able to proceed further:
const output =arr1.filter((item) => {
return arr2.indexOf(item.POSITION) !== -1 && (item.POSITION)
});

I guess you can use map to create a new array. There you can use find to get the proper LEVEL property for the current POSITION.
One smart solution can be the following:
const positions = [{"LEVEL":4,"POSITION":"RGM"},{"LEVEL":5,"POSITION":"GM"},{"LEVEL":5,"POSITION":"GMH"}];
const emails = [{"EMAIL":"test1#stc.com","POSITION":"GM"},{"EMAIL":"test2#stc.com","POSITION":"GMH"},{"EMAIL":"test3#stc.com","POSITION":"RGM"},{"EMAIL":"test3#CSR.COM.AU","POSITION":"GM"}];
const result = emails.map(email => {
email['LEVEL'] = positions.find(p => p['POSITION'] === email['POSITION'])['LEVEL'];
return email;
})
console.log(result);
From Array.prototype.map() documentation:
The map() method creates a new array with the results of calling a provided function on every element in the calling array.
From Array.prototype.find() documentation:
The find() method returns the value of the first element in the provided array that satisfies the provided testing function.
I hope this helps!

Another solution for beginners:
let arr1 = [{"LEVEL":4,"POSITION":"RGM"},{"LEVEL":5,"POSITION":"GM"},{"LEVEL":5,"POSITION":"GMH"}]
let arr2 = [{"EMAIL":"test1#stc.com","POSITION":"GM"},
{"EMAIL":"test2#stc.com","POSITION":"GMH"},
{"EMAIL":"test3#stc.com","POSITION":"RGM"},
{"EMAIL":"test3#CSR.COM.AU","POSITION":"GM"}]
function addLevel() {
const resultingArray = [];
arr2.forEach(itemarray2 => {
const copyOfArrayItem2 = Object.assign({}, itemarray2);
resultingArray.push(copyOfArrayItem2);
const itemArray1 = arr1.find(x => x.POSITION === itemarray2.POSITION);
if(itemArray1) {
copyOfArrayItem2.LEVEL = itemArray1.LEVEL;
}
});
return resultingArray;
}
const newArray = addLevel();
console.log(newArray);

You could take a Map and map new objects with LEVEL.
var array1 = [{ LEVEL: 4, POSITION: "RGM" }, { LEVEL: 5, POSITION: "GM" }, { LEVEL: 5, POSITION: "GMH" }],
array2 = [{ EMAIL: "test1#stc.com", POSITION: "GM" }, { EMAIL: "test2#stc.com", POSITION: "GMH" }, { EMAIL: "test3#stc.com", POSITION: "RGM" }, { EMAIL: "test3#CSR.COM.AU", POSITION: "GM" }],
levels = array1.reduce((m, { LEVEL, POSITION }) => m.set(POSITION, LEVEL), new Map),
result = array2.map(o => Object.assign({ LEVEL: levels.get(o.POSITION) }, o));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

The simplest way is:
let arr1 = [{"LEVEL":4,"POSITION":"RGM"},{"LEVEL":5,"POSITION":"GM"},{"LEVEL":5,"POSITION":"GMH"}]
let arr2 = [{"EMAIL":"test1#stc.com","POSITION":"GM"},
{"EMAIL":"test2#stc.com","POSITION":"GMH"},
{"EMAIL":"test3#stc.com","POSITION":"RGM"},
{"EMAIL":"test3#CSR.COM.AU","POSITION":"GM"}]
let output = arr1.map(item => {
item.Email = arr2.find(a => {
return a.POSITION === item.POSITION
}).EMAIL;
return item;
});
console.log(output);

Related

Filter or compare a value in the same array [duplicate]

I have an array like this:
const array=[ {id:0, quantity:1}, {id:1, quantity:2}, {id:0, quantity:4} ]
My GOAL is to be like this:
const array=[ {id:1, quantity:2}, {id:0, quantity:4} ]
The order of the object does not matter as long as it can find the 'id' with the larger quantity
I tried filter + findIndex, map +filter, etc but I kept making mistake. I need help.
You could use a hash table and check if an object with the same id is in the result set. If the actual quantity is greater, assign the actual object.
var array = [{ id: 0, quantity: 1 }, { id: 1, quantity: 2 }, { id: 0, quantity: 4 }],
hash = Object.create(null),
unique = array.reduce(function (r, o) {
if (!(o.id in hash)) {
hash[o.id] = r.push(o) - 1;
return r;
}
if (o.quantity > r[hash[o.id]].quantity) {
r[hash[o.id]] = o;
}
return r;
}, []);
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let's get all the ids first. Then for each unique id, we'll find all the relevant objects in the array (using filter) and get the maximum of all their quantity properties.
function uniqByMax(arr) {
const ids = arr.map(elt => elt.id);
const uniqueIds = uniq(ids);
return uniqueIds.map(id => {
const matchingElts = arr.filter(elt => elt.id === id);
const quantities = matchingElts.map(elt => elt.quantity);
const quantity = Math.max(...quantities);
return {id, quantity};
});
}
You can grab uniq off the net somewhere, or use a library, or write it yourself.
Here is another approach, which uses a filter with side effects, if that is your cup of tea:
function uniqByMax(arr) {
return arr.filter(elt => {
const previous = arr.find(e2 => e2.id === elt.id);
if (previous === elt) return true;
previous.quantity = Math.max(previous.quantity, elt.quantity);
});
}
The basic idea is to loop through the elements. For each element, we find if there is an earlier element with the same id. If not, retain this element (return true;); otherwise, update the quantity of the earlier element we retained with the maximum of its quantity and this element's quantity.
In the interest of generality, it could be interesting to parameterize this function by the property we are finding unique values of, and the way to update/combine multiple items:
function uniqTransform(arr, prop, combine) {
return arr.filter(elt => {
const previous = arr.find(e2 => e2[prop] === elt[prop]);
if (previous === elt) return true;
combine(previous, elt);
});
Then we call this with
uniqTransform(arr, 'id', (a, b) => a.quantity = Math.max(a.quantity, b.quantity));
Or we could generalize it further by using another function to identify elements which are supposed to be considered the same for uniqueifying purposes, which we will call uniqueFunc:
function uniqTransform(arr, uniqueFunc, combineFunc) {
return arr.filter(elt => {
const previous = arr.find(e2 => uniqueFunc(elt, e2));
if (previous === elt) return true;
combineFunc(previous, elt);
});
Then we call this with
uniqTransform(
arr,
(a, b) => a.id === b.id,
(a, b) => a.quantity = Math.max(a.quantity, b.quantity));
Here you go.
const array=[ {id:0, quantity:1}, {id:1, quantity:2}, {id:0, quantity:4} ];
const arrayFiltered = [];
array.forEach(obj => {
const item = arrayFiltered.find(thisItem => thisItem.id === obj.id);
if (item) {
if (item.quantity < obj.quantity) {
item.quantity = obj.quantity;
}
return;
}
arrayFiltered.push(obj);
});
console.log(arrayFiltered);
Can you work with objects? There's a simple alternative using this code
const array = [{id:0, quantity:1}, {id:1, quantity:2}, {id:0, quantity:4}]
const object = {}
array.forEach((element) => {
object[element.id] = element
})
The only problem is that you will override the previous element each time you found an element with the same id.

Loop into array object then check if there's a equal value?

Question, I have this array object, I want to find out which of this array have a similar values
then make them as one.
Example
[0:
cartProduct: {
category: "chair"
color: "navy"
id: "628a1738fd8299ae6659d994"
image: "http://localhost:5000/../public/Product_chair_communal-navy.jpg"
name: "The Communal"
price: "4.30"
}
quantity: 1,
1:
cartProduct: {{
category: "chair"
color: "navy"
id: "628a1738fd8299ae6659d994"
image: "http://localhost:5000/../public/Product_chair_communal-navy.jpg"
name: "The Communal"
price: "4.30"
}
quantity: 1,
]
For example the data above I want to know if they have the similar values interms of color if yes then only return one value.
Thanks!
You can use this loop:
let uniqueArray = [];
dataArray.forEach((item, indx) => {
let colorsArray = [];
if (colorsArray.includes(item.color)) {
continue;
}
uniqueArray.push(item);
})
Not the cleanest, or most performant approach:
// function to group the items
const groupCartItems = (items, byProperties = ['color', 'id']) => {
// utility funciton
const verifyEquality = (itemA, itemB) => {
let isEqual = true;
byProperties.forEach((prop) => {
if (itemA.cartProduct[prop] != itemB.cartProduct[prop]) {
isEqual = false;
break;
}
});
return isEqual;
};
const groupedItems = [];
items.forEach((item) => {
// if item has been added, skip
if (groupedItems.find((i) => verifyEquality(item, i))) {
return;
}
// find equal items
const equals = items.filter((i) => verifyEquality(item, i));
// sum quantities
const quantity = equals.reduce((previousValue, data) => previousValue + data.quantity, 0);
// push
groupedItems.push({
cartProduct: item.cartProduct,
quantity,
});
});
return groupedItems;
};
For the 'similarity' stuff, I would recommend not to do this, because it is just not a good practise. Have your values equal or else!!!
Now seriously, check string-similarity. From documentation, you would only need to change the if inside verifyEquality function to:
import stringSimilarity from 'string-similarity';
// tweak this value to change how similar strings should be to be considered equal
const EQUALITY_RATIO = 0.75;
// ....
if (stringSimilarity.compareTwoStrings(itemA.cartProduct[prop], itemB.cartProduct[prop]) < EQUALITY_RATIO) {
}

Fastest way to clean path collisions in array of strings

This is a hard one to explain, but here goes. I need to clean an array of 'path' strings where if a path has sub properties it not include the top level property. but only the child properties
E.g
[
'firstName',
'address',
'address.local.addressLine1',
'address.local.addressLine2',
'address.local',
]
Should become:
[
'firstName',
'address.local.addressLine1',
'address.local.addressLine2',
'address.local',
]
I have a fairly verbose function kind of working so far, but looking to see if there is a more elegant/better solution than this:
function cleanCollisions(array) {
var output = [];
// return [...new Set(array)];
var map = array.reduce(function(set, field) {
if (!Boolean(field)) {
return set;
}
////////////////
var rootKey = field.split('.')[0];
if(!set[rootKey]) {
set[rootKey] =[];
}
var count = field.split('.').length -1;
if(count) {
set[rootKey].push(field);
}
return set;
}, {})
for(const key in map) {
value = map[key];
if(value.length) {
output.push(value);
} else {
output.push(key);
}
}
////////////////
return output.flat();
}
I'd first iterate over the array to extract the top property of all strings that have sub properties, then filter out all those top properties.
const input = [
'firstName',
'address',
'address.local.addressLine1',
'address.local.addressLine2',
'address.local',
];
const topLevelProps = new Set();
for (const str of input) {
const match = str.match(/^(.*?)\./);
if (match) {
topLevelProps.add(match[1]);
}
}
const output = input.filter(str => !topLevelProps.has(str));
console.log(output);
A variation of the answer by CertainPerformance but using filter and map instead of regex:
const paths = [
'firstName',
'address',
'address.local.addressLine1',
'address.local.addressLine2',
'address.local',
];
const roots = paths.filter(p => p.includes('.')).map(p => p.split('.')[0]);
const cleansed = paths.filter(p => p.includes('.') || !roots.includes(p));
console.log(cleansed);

Can I add or edit object in array by field [duplicate]

I have this javascript object:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
I need to replace objects in arr1 with items from arr2 with same id.
So here is the result I want to get:
var arr1 = [{id:'124',name:'ttt'},
{id:'589',name:'www'},
{id:'45',name:'yyy'},
{id:'567',name:'rrr'}]
How can I implement it using javascript?
You can use Array#map with Array#find.
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}];
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var res = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
console.log(res);
Here, arr2.find(o => o.id === obj.id) will return the element i.e. object from arr2 if the id is found in the arr2. If not, then the same element in arr1 i.e. obj is returned.
What's wrong with Object.assign(target, source) ?
Arrays are still type object in Javascript, so using assign should still reassign any matching keys parsed by the operator as long as matching keys are found, right?
There is always going to be a good debate on time vs space, however these days I've found using space is better for the long run.. Mathematics aside let look at a one practical approach to the problem using hashmaps, dictionaries, or associative array's whatever you feel like labeling the simple data structure..
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
I like this approach because though you could argue with an array with low numbers you are wasting space because an inline approach like #Tushar approach performs indistinguishably close to this method. However I ran some tests and the graph shows how performant in ms both methods perform from n 0 - 1000. You can decide which method works best for you, for your situation but in my experience users don't care to much about small space but they do care about small speed.
Here is my performance test I ran for source of data
var n = 1000;
var graph = new Array();
for( var x = 0; x < n; x++){
var arr1s = [...Array(x).keys()];
var arr2s = arr1s.filter( e => Math.random() > .5);
var arr1 = arr1s.map(e => {return {id: e, name: 'bill'}});
var arr2 = arr2s.map(e => {return {id: e, name: 'larry'}});
// Map 1
performance.mark('p1s');
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
performance.mark('p1e');
// Map 2
performance.mark('p2s');
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
performance.mark('p2e');
graph.push({ x: x, r1: performance.measure('HashMap Method', 'p1s', 'p1e').duration, r2: performance.measure('Inner Find', 'p2s','p2e').duration});
}
Since you're using Lodash you could use _.map and _.find to make sure major browsers are supported.
In the end I would go with something like:
function mergeById(arr) {
return {
with: function(arr2) {
return _.map(arr, item => {
return _.find(arr2, obj => obj.id === item.id) || item
})
}
}
}
var result = mergeById([{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}])
.with([{id:'124',name:'ttt'}, {id:'45',name:'yyy'}])
console.log(result);
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
Thanks to ES6 we can made it with easy way -> for example on util.js module ;))).
Merge 2 array of entity
export const mergeArrays = (arr1, arr2) =>
arr1 && arr1.map(obj => arr2 && arr2.find(p => p.id === obj.id) || obj);
gets 2 array and merges it.. Arr1 is main array which is priority is
high on merge process
Merge array with same type of entity
export const mergeArrayWithObject = (arr, obj) => arr && arr.map(t => t.id === obj.id ? obj : t);
it merges the same kind of array of type with some kind of type for
example: array of person ->
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Uc"}]
second param Person {id:3, name: "Name changed"}
result is
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Name changed"}]
I like to go through arr2 with foreach() and use findIndex() for checking for occurrence in arr1:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
arr2.forEach(element => {
const itemIndex = arr1.findIndex(o => o.id === element.id);
if(itemIndex > -1) {
arr1[itemIndex] = element;
} else {
arr1 = arr1.push(element);
}
});
console.log(arr1)
I'd like to suggest another solution:
const objectToReplace = this.array.find(arrayItem => arrayItem.id === requiredItem.id);
Object.assign(objectToReplace, newObject);
Considering that the accepted answer is probably inefficient for large arrays, O(nm), I usually prefer this approach, O(2n + 2m):
function mergeArrays(arr1 = [], arr2 = []){
//Creates an object map of id to object in arr1
const arr1Map = arr1.reduce((acc, o) => {
acc[o.id] = o;
return acc;
}, {});
//Updates the object with corresponding id in arr1Map from arr2,
//creates a new object if none exists (upsert)
arr2.forEach(o => {
arr1Map[o.id] = o;
});
//Return the merged values in arr1Map as an array
return Object.values(arr1Map);
}
Unit test:
it('Merges two arrays using id as the key', () => {
var arr1 = [{id:'124',name:'qqq'}, {id:'589',name:'www'}, {id:'45',name:'eee'}, {id:'567',name:'rrr'}];
var arr2 = [{id:'124',name:'ttt'}, {id:'45',name:'yyy'}];
const actual = mergeArrays(arr1, arr2);
const expected = [{id:'124',name:'ttt'}, {id:'589',name:'www'}, {id:'45',name:'yyy'}, {id:'567',name:'rrr'}];
expect(actual.sort((a, b) => (a.id < b.id)? -1: 1)).toEqual(expected.sort((a, b) => (a.id < b.id)? -1: 1));
})
// here find all the items that are not it the arr1
const temp = arr1.filter(obj1 => !arr2.some(obj2 => obj1.id === obj2.id))
// then just concat it
arr1 = [...temp, ...arr2]
Here a more transparent approach. I find the oneliners harder to read and harder to debug.
export class List {
static replace = (object, list) => {
let newList = [];
list.forEach(function (item) {
if (item.id === object.id) {
newList.push(object);
} else {
newList.push(item);
}
});
return newList;
}
}
If you don't care about the order of the array, then you may want to get the difference between arr1 and arr2 by id using differenceBy() and then simply use concat() to append all the updated objects.
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}]
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>
I am only submitting this answer because people expressed concerns over browsers and maintaining the order of objects. I recognize that it is not the most efficient way to accomplish the goal.
Having said this, I broke the problem down into two functions for readability.
// The following function is used for each itertion in the function updateObjectsInArr
const newObjInInitialArr = function(initialArr, newObject) {
let id = newObject.id;
let newArr = [];
for (let i = 0; i < initialArr.length; i++) {
if (id === initialArr[i].id) {
newArr.push(newObject);
} else {
newArr.push(initialArr[i]);
}
}
return newArr;
};
const updateObjectsInArr = function(initialArr, newArr) {
let finalUpdatedArr = initialArr;
for (let i = 0; i < newArr.length; i++) {
finalUpdatedArr = newObjInInitialArr(finalUpdatedArr, newArr[i]);
}
return finalUpdatedArr
}
const revisedArr = updateObjectsInArr(arr1, arr2);
jsfiddle
function getMatch(elem) {
function action(ele, val) {
if(ele === val){
elem = arr2[i];
}
}
for (var i = 0; i < arr2.length; i++) {
action(elem.id, Object.values(arr2[i])[0]);
}
return elem;
}
var modified = arr1.map(getMatch);
I went with this, because it makes sense to me. Comments added for readers!
masterData = [{id: 1, name: "aaaaaaaaaaa"},
{id: 2, name: "Bill"},
{id: 3, name: "ccccccccc"}];
updatedData = [{id: 3, name: "Cat"},
{id: 1, name: "Apple"}];
updatedData.forEach(updatedObj=> {
// For every updatedData object (dataObj), find the array index in masterData where the IDs match.
let indexInMasterData = masterData.map(masterDataObj => masterDataObj.id).indexOf(updatedObj.id); // First make an array of IDs, to use indexOf().
// If there is a matching ID (and thus an index), replace the existing object in masterData with the updatedData's object.
if (indexInMasterData !== undefined) masterData.splice(indexInMasterData, 1, updatedObj);
});
/* masterData becomes [{id: 1, name: "Apple"},
{id: 2, name: "Bill"},
{id: 3, name: "Cat"}]; as you want.`*/
The accepted answer using array.map is correct but you have to remember to assign it to another variable since array.map doesnt change original array, it actually creates a new array.
//newArr contains the mapped array from arr2 to arr1.
//arr1 still contains original value
var newArr = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
Array.prototype.update = function(...args) {
return this.map(x=>args.find((c)=>{return c.id===x.id}) || x)
}
const result =
[
{id:'1',name:'test1'},
{id:'2',name:'test2'},
{id:'3',name:'test3'},
{id:'4',name:'test4'}
]
.update({id:'1',name:'test1.1'}, {id:'3',name:'test3.3'})
console.log(result)
This is how I do it in TypeScript:
const index = this.array.indexOf(this.objectToReplace);
this.array[index] = newObject;

Replacing objects in array

I have this javascript object:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
I need to replace objects in arr1 with items from arr2 with same id.
So here is the result I want to get:
var arr1 = [{id:'124',name:'ttt'},
{id:'589',name:'www'},
{id:'45',name:'yyy'},
{id:'567',name:'rrr'}]
How can I implement it using javascript?
You can use Array#map with Array#find.
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}];
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var res = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
console.log(res);
Here, arr2.find(o => o.id === obj.id) will return the element i.e. object from arr2 if the id is found in the arr2. If not, then the same element in arr1 i.e. obj is returned.
What's wrong with Object.assign(target, source) ?
Arrays are still type object in Javascript, so using assign should still reassign any matching keys parsed by the operator as long as matching keys are found, right?
There is always going to be a good debate on time vs space, however these days I've found using space is better for the long run.. Mathematics aside let look at a one practical approach to the problem using hashmaps, dictionaries, or associative array's whatever you feel like labeling the simple data structure..
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
I like this approach because though you could argue with an array with low numbers you are wasting space because an inline approach like #Tushar approach performs indistinguishably close to this method. However I ran some tests and the graph shows how performant in ms both methods perform from n 0 - 1000. You can decide which method works best for you, for your situation but in my experience users don't care to much about small space but they do care about small speed.
Here is my performance test I ran for source of data
var n = 1000;
var graph = new Array();
for( var x = 0; x < n; x++){
var arr1s = [...Array(x).keys()];
var arr2s = arr1s.filter( e => Math.random() > .5);
var arr1 = arr1s.map(e => {return {id: e, name: 'bill'}});
var arr2 = arr2s.map(e => {return {id: e, name: 'larry'}});
// Map 1
performance.mark('p1s');
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
performance.mark('p1e');
// Map 2
performance.mark('p2s');
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
performance.mark('p2e');
graph.push({ x: x, r1: performance.measure('HashMap Method', 'p1s', 'p1e').duration, r2: performance.measure('Inner Find', 'p2s','p2e').duration});
}
Since you're using Lodash you could use _.map and _.find to make sure major browsers are supported.
In the end I would go with something like:
function mergeById(arr) {
return {
with: function(arr2) {
return _.map(arr, item => {
return _.find(arr2, obj => obj.id === item.id) || item
})
}
}
}
var result = mergeById([{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}])
.with([{id:'124',name:'ttt'}, {id:'45',name:'yyy'}])
console.log(result);
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
I'd like to suggest another solution:
const objectToReplace = this.array.find(arrayItem => arrayItem.id === requiredItem.id);
Object.assign(objectToReplace, newObject);
Thanks to ES6 we can made it with easy way -> for example on util.js module ;))).
Merge 2 array of entity
export const mergeArrays = (arr1, arr2) =>
arr1 && arr1.map(obj => arr2 && arr2.find(p => p.id === obj.id) || obj);
gets 2 array and merges it.. Arr1 is main array which is priority is
high on merge process
Merge array with same type of entity
export const mergeArrayWithObject = (arr, obj) => arr && arr.map(t => t.id === obj.id ? obj : t);
it merges the same kind of array of type with some kind of type for
example: array of person ->
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Uc"}]
second param Person {id:3, name: "Name changed"}
result is
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Name changed"}]
I like to go through arr2 with foreach() and use findIndex() for checking for occurrence in arr1:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
arr2.forEach(element => {
const itemIndex = arr1.findIndex(o => o.id === element.id);
if(itemIndex > -1) {
arr1[itemIndex] = element;
} else {
arr1 = arr1.push(element);
}
});
console.log(arr1)
Considering that the accepted answer is probably inefficient for large arrays, O(nm), I usually prefer this approach, O(2n + 2m):
function mergeArrays(arr1 = [], arr2 = []){
//Creates an object map of id to object in arr1
const arr1Map = arr1.reduce((acc, o) => {
acc[o.id] = o;
return acc;
}, {});
//Updates the object with corresponding id in arr1Map from arr2,
//creates a new object if none exists (upsert)
arr2.forEach(o => {
arr1Map[o.id] = o;
});
//Return the merged values in arr1Map as an array
return Object.values(arr1Map);
}
Unit test:
it('Merges two arrays using id as the key', () => {
var arr1 = [{id:'124',name:'qqq'}, {id:'589',name:'www'}, {id:'45',name:'eee'}, {id:'567',name:'rrr'}];
var arr2 = [{id:'124',name:'ttt'}, {id:'45',name:'yyy'}];
const actual = mergeArrays(arr1, arr2);
const expected = [{id:'124',name:'ttt'}, {id:'589',name:'www'}, {id:'45',name:'yyy'}, {id:'567',name:'rrr'}];
expect(actual.sort((a, b) => (a.id < b.id)? -1: 1)).toEqual(expected.sort((a, b) => (a.id < b.id)? -1: 1));
})
// here find all the items that are not it the arr1
const temp = arr1.filter(obj1 => !arr2.some(obj2 => obj1.id === obj2.id))
// then just concat it
arr1 = [...temp, ...arr2]
Here a more transparent approach. I find the oneliners harder to read and harder to debug.
export class List {
static replace = (object, list) => {
let newList = [];
list.forEach(function (item) {
if (item.id === object.id) {
newList.push(object);
} else {
newList.push(item);
}
});
return newList;
}
}
If you don't care about the order of the array, then you may want to get the difference between arr1 and arr2 by id using differenceBy() and then simply use concat() to append all the updated objects.
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}]
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>
I am only submitting this answer because people expressed concerns over browsers and maintaining the order of objects. I recognize that it is not the most efficient way to accomplish the goal.
Having said this, I broke the problem down into two functions for readability.
// The following function is used for each itertion in the function updateObjectsInArr
const newObjInInitialArr = function(initialArr, newObject) {
let id = newObject.id;
let newArr = [];
for (let i = 0; i < initialArr.length; i++) {
if (id === initialArr[i].id) {
newArr.push(newObject);
} else {
newArr.push(initialArr[i]);
}
}
return newArr;
};
const updateObjectsInArr = function(initialArr, newArr) {
let finalUpdatedArr = initialArr;
for (let i = 0; i < newArr.length; i++) {
finalUpdatedArr = newObjInInitialArr(finalUpdatedArr, newArr[i]);
}
return finalUpdatedArr
}
const revisedArr = updateObjectsInArr(arr1, arr2);
jsfiddle
function getMatch(elem) {
function action(ele, val) {
if(ele === val){
elem = arr2[i];
}
}
for (var i = 0; i < arr2.length; i++) {
action(elem.id, Object.values(arr2[i])[0]);
}
return elem;
}
var modified = arr1.map(getMatch);
I went with this, because it makes sense to me. Comments added for readers!
masterData = [{id: 1, name: "aaaaaaaaaaa"},
{id: 2, name: "Bill"},
{id: 3, name: "ccccccccc"}];
updatedData = [{id: 3, name: "Cat"},
{id: 1, name: "Apple"}];
updatedData.forEach(updatedObj=> {
// For every updatedData object (dataObj), find the array index in masterData where the IDs match.
let indexInMasterData = masterData.map(masterDataObj => masterDataObj.id).indexOf(updatedObj.id); // First make an array of IDs, to use indexOf().
// If there is a matching ID (and thus an index), replace the existing object in masterData with the updatedData's object.
if (indexInMasterData !== undefined) masterData.splice(indexInMasterData, 1, updatedObj);
});
/* masterData becomes [{id: 1, name: "Apple"},
{id: 2, name: "Bill"},
{id: 3, name: "Cat"}]; as you want.`*/
The accepted answer using array.map is correct but you have to remember to assign it to another variable since array.map doesnt change original array, it actually creates a new array.
//newArr contains the mapped array from arr2 to arr1.
//arr1 still contains original value
var newArr = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
Array.prototype.update = function(...args) {
return this.map(x=>args.find((c)=>{return c.id===x.id}) || x)
}
const result =
[
{id:'1',name:'test1'},
{id:'2',name:'test2'},
{id:'3',name:'test3'},
{id:'4',name:'test4'}
]
.update({id:'1',name:'test1.1'}, {id:'3',name:'test3.3'})
console.log(result)
This is how I do it in TypeScript:
const index = this.array.indexOf(this.objectToReplace);
this.array[index] = newObject;

Categories

Resources