Compare and update two arrays without losing mutated data - javascript

I have an array of objects contains data of persons
const oldArr = [
{
id: 1,
name: 'Alex',
},
{
id: 2,
name: 'John',
},
{
id: 3,
name: 'Jack',
}
]
then I add data to this array to each element where I end up with new key called money with value of 20 as the following
oldArr.map((el, index) => el.money = 20)
and the array becomes like this
...
{
id: 2,
name: 'John',
money: 20
},
...
Now, I have a new array with new data (new person) but missing the money I have added before. (careful person with id 2 is not there)
const newArr = [
{
id: 1,
name: 'Alex',
},
{
id: 3,
name: 'Jack',
},
{
id: 4,
name: 'Chris',
},
]
I want to update the old array with new data but also keep the mutated data, and I want the result to end up like this:
const result = [
{
id: 1,
name: 'Alex',
money: 20
},
{
id: 3,
name: 'Jack',
money: 20
},
{
id: 4,
name: 'Chris',
},
]
Thanks for the help.

Just a note: map creates a whole new array, it doesn't make sense to use it for just mutating the contents. Use forEach or just a regular for loop instead.
oldArr.forEach((el) => (el.money = 20));
The following will give you the intended result:
const result = newArr.map(
(newEl) => oldArr.find((el) => el.id === newEl.id) || newEl
);
The OR operator || returns the second argument if the first is falsey.
You can optimize this by mapping items by id instead of brute force searching the old array.
const idMap = new Map();
oldArr.forEach((el) => {
el.money = 20;
idMap.set(el.id, el);
});
const result = newArr.map((newEl) => idMap.get(newEl.id) || newEl);
Stackblitz: https://stackblitz.com/edit/js-f3sw8w?file=index.js

If I getted it clear you are just trying to iterate throw the items of array generating a new array with the property "money" added to each one.
If so the map is the best option, just assign it to a new variable and change the item before return the element like bellow.
const oldArr = [
{
id: 1,
name: "Alex"
},
{
id: 2,
name: "John"
},
{
id: 3,
name: "Jack"
}
];
const newArr = oldArr.map((el) => {
el.money = "20";
return el;
});
console.log(oldArr);
console.log(newArr);
In this way you'll be able to keep both arrays.
If wasn't this, pls let me know.

Just merge the objects:
const result = oldArr.map((person) => ({
...person,
...newArr.find((cur) => cur.id === person.id),
}));

Related

Replace/Update The Existing Array of Object that Matches the Incoming Array

I'm trying to find and replace the array if an incoming arrays matches the existing one but unfortunately, I'm stucked with the some
Here's my existing array.
let existingData = [{
id: 1,
product: 'Soap',
price: '$2'
},{
id: 2,
product: 'Sofa',
price: '$30'
},{
id: 3,
product: 'Chair',
price: '$45'
}]
And here's my incoming array.
const updateData = [{
id: 1,
product: 'Soap',
price: '$3'
},{
id: 2,
product: 'Sofa',
price: '$35'
}]
So far, I saw the foreach but unfortunately, I'm not sure how can I use it if the term is an array. But I get stuck and I can't proceed.
const updateData = [{
id: 1,
product: 'Soap',
price: '$3'
},{
id: 2,
product: 'Sofa',
price: '$35'
}]
existingData.forEach(d=>{
if(d.id === ??? how can I match this to the incoming array?)
// if matches, then update the existing data with the updated one.
})
And the expected result must be something like this:
let existingData = [{
id: 1,
product: 'Soap',
price: '$3'
},{
id: 2,
product: 'Sofa',
price: '$35'
},{
id: 3,
product: 'Chair',
price: '$45'
}]
If in some cases, the data is not present in the existingData, then the incoming array will just add simply in the existing array.
Please help how can I achieve it and if there's a better and cleaner way to do this, please let me know. Thank you!
You can easily achieve this result using forEach and find
let existingData = [{
id: 1,
product: "Soap",
price: "$2",
},
{
id: 2,
product: "Sofa",
price: "$30",
},
{
id: 3,
product: "Chair",
price: "$45",
},
];
const updateData = [{
id: 1,
product: "Soap",
price: "$3",
},
{
id: 2,
product: "Sofa",
price: "$35",
},
];
updateData.forEach((obj) => {
let isExist = existingData.find((o) => o.id === obj.id);
if (isExist) {
isExist.price = obj.price;
isExist.product = obj.product;
}
});
console.log(existingData);
If there are multiple properties that need to be updated then you can use for..in loop over the updated object and replace the prop in the existing property.
updateData.forEach((obj) => {
let isExist = existingData.find((o) => o.id === obj.id);
if (isExist) {
for (let prop in obj) {
isExist[prop] = obj[prop];
}
}
});
If you want to add the data if it doesn't exist in the existing array then you need to push it into existingData array.
let existingData = [{
id: 1,
product: "Soap",
price: "$2",
},
{
id: 2,
product: "Sofa",
price: "$30",
},
{
id: 3,
product: "Chair",
price: "$45",
},
];
const updateData = [{
id: 1,
product: "Soap",
price: "$3",
},
{
id: 2,
product: "Sofa",
price: "$35",
},
{
id: 6,
product: "Sofa",
price: "$135",
},
];
updateData.forEach((obj) => {
let isExist = existingData.find((o) => o.id === obj.id);
if (isExist) {
for (let prop in obj) {
isExist[prop] = obj[prop];
}
} else {
existingData.push(obj);
}
});
console.log(existingData);
existingData.forEach(existingItem => {
let item = updatedDate.find(u => u.id === existingItem.id);
if(item){
existingItem.product = item.product;
existingItem.price= item.price;
}
});
Given your existingData and updateData, you can quite simply do something like this:
// form a temporary object mapping updated objects' ids to the new ids
const updateDataByKeys = Object.fromEntries(updateData.map(e => [e.id, e]));
// map through `existingData`, replacing old entries with updated where they
// exist in the above temporary object, using the old object if they don't.
const newData = existingData.map(e => updateDataByKeys[e.id] || e);
Creating the temporary object should make this approach quite a bit faster than approaches using .find() on updateData.
If you need to merge the data from updateData into the existing objects, you could do
const newData = existingData.map(
e => updateDataByKeys[e.id] ? ({...e, ...updateDataByKeys[e.id]}) : e
);
EDIT: Based on comments, if you also need to add new objects from updateData:
// form a temporary object mapping updated objects' ids to the new ids
const updateDataByKeys = Object.fromEntries(updateData.map(e => [e.id, e]));
// Map through `existingData`, replacing old entries with updated where they
// exist in the above temporary object, using the old object if they don't.
// When using an update object, removes it from the mapping; the left-over
// new data (which had no ID in the old data) are then concatenated to the
// list.
const newData = existingData.map(e => {
if(updateDataByKeys[e.id]) {
const val = updateDataByKeys[e.id];
delete updateDataByKeys[e.id];
return val;
}
return e;
}).concat(Object.values(updateDataByKeys));

Delete multiple objects in an array by id

I have a main array of objects with each object having some key/values as well as a "id" key with 1,2,3,4,5, etc
Now I have another array representing just id's (like [2,3])
I want to use this array to delete objects from the main array...so in this case, objects from the main array having id's 2 & 3 should be deleted
While I am aware of findBy(id), I am not sure if that can be used to delete multiple objects at once.
You can use filter. In the filter callback function check if the id is also there in id array by using includes
let idArr = [1, 2]
let obj = [{
id: 1,
name: 'abc'
},
{
id: 2,
name: 'abc'
},
{
id: 3,
name: 'abc'
},
{
id: 4,
name: 'abc'
}
];
let data = obj.filter(item => !idArr.includes(item.id));
console.log(data);
console.log(obj)
using filter might work well here. you could write something like:
var newArray = oldArray.filter(object => !ids.includes(object.id))
You can do it, like this:
[2,3].forEach(key => {
delete object[key];
})
You can use filter method for this.
Ex:
let id = 2;
let list = [{
Id: 1,
Name: 'a'
}, {
Id: 2,
Name: 'b'
}, {
Id: 3,
Name: 'c'
}];
let lists = list.filter(x => {
return x.Id != id;
})
console.log(lists);
Assuming you want to delete items from the original array by entirely removing the element from the array (and you don't want to get a new array), you can take advantage of
Array.splice
let idArr = [1, 2];
let obj = [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
for (let id of idArr) {
// look for the element by its id.
const objIdRef = obj.find(i => i.id === id);
// if it actually exists, splice it.
objIdRef && obj.splice(obj.indexOf(objIdRef), 1);
}
console.log(obj);
If the obj array is big, you might want to make a map from it before processing the id array, so that the complexing is reduced to O(1) when the delete process begins.
Perhaps This is what you want:
var arr= [{id:1, name: "foo"}, {id:2, name: "bar"}, {id:3, name:"not to be deleted"}];
var idsToDelete = [1, 2];
var res = arr.map((i, idx)=>{
return arr[idx] = idsToDelete.includes(i.id)? undefined : arr[idx]
}).filter(i=>i)
console.log(res)
You can try Lodash.js functions _.forEach() and _.remove()
let valuesArr = [
{id: 1, name: "dog"},
{id: 2, name: "cat"},
{id: 3, name: "rat"},
{id: 4, name: "bat"},
{id: 5, name: "pig"},
];
let removeValFromIndex = [
{id: 2, name: "cat"},
{id: 5, name: "pig"},
];
_.forEach(removeValFromIndex, (indi) => {
_.remove(valuesArr, (item) => {
return item.id === indi.id;
});
})
console.log(valuesArr)
/*[
{id: 1, name: "dog"},
{id: 3, name: "rat"},
{id: 4, name: "bat"},
]; */
Don't forget to clone (_.clone(valuesArr) or [...valuesArr]) before mutate your array

JavaScript Array push() method use to the id as the index value?

I want to use id as the index value and then generate a new array.
What better way do you have?
This is the result I want
Arr:Array[2]
3:"a"
8:"b"
Before processing
Arr:Array[2]
0:"a"
1:"b"
My code
var data = [{
id: 3,
name: 'a'
},
{
id: 8,
name: 'b',
}
]
var arr = []
const p = data.map(item => {
arr[item.id].push(item.name)
})
console.log(p)
You could use reduce, initialised with an empty array, set each index with id, and each value with name:
var data = [{
id: 3,
name: 'a'
},
{
id: 8,
name: 'b',
}
]
console.log(data.reduce((a, {id, name}) => (a[id] = name, a), []))
NOTE, you cannot have an array without indexes between values. Javascript will automatically fill these with undefined
If this doesn't fit your needs, then the only other option is to use an object (or a map but that's more complicated :P), which can still act like an array in a sense:
var data = [{
id: 3,
name: 'a'
},
{
id: 8,
name: 'b',
}
]
const obj = data.reduce((a, {id, name}) => (a[id] = name, a), {})
console.log(obj)
console.log(obj[3]) // a
console.log(obj[8]) // b

Comparing two Objects by id and creating the new one

So I have two objects with this structure:
const obj1 = { data:
[ {
id: 1,
name: 'Linda'
},
{
id: 2,
name: 'Mark'
}
];
const obj2 = [
{
id: 1,
salary: "2000, 60 USD"
},
undefined
],
[
{
id: 2,
salary: "4000, 50 USD"
},
undefined
]
I need to make a function to combine both of these into one object, based on id.
So the final results would be:
const finalObj = { data:
[ {
id: 1,
name: 'Linda',
salary: "2000, 60 USD"
},
{
id: 2,
name: 'Mark',
salary: "4000, 50 USD"
}
];
I have checked other questions, but could not find anything that would help. It can be done with lodash afaik, but don't know how.
I have tried the following:
finalObj = obj1.data.map(x => {
return {
...x,
...obj2
}
But it didn't map correctly.
Thanks.
EDIT: Updated obj2 response.
You can array#concat both your array and then using array#reduce and an object lookup with id, merge your objects. Then return all the values from this object.
const obj1 = { data: [{ id: 1, name: 'Linda' }, { id: 2, name: 'Mark' }]},
obj2 = { data: [{ id: 1, salary: "2000, 60 USD"}, { id: 2, salary: "4000, 50 USD"}]},
result = Object.values(obj1.data.concat(obj2.data).reduce((r,o) => {
r[o.id] = r[o.id] || {};
r[o.id] = {...r[o.id], ...o};
return r;
},{}));
console.log(result);
You could take a Map for collecting all properties of the same id in an object. Later get the values of the map.
const join = o => o && map.set(o.id, Object.assign(map.get(o.id) || {}, o));
var obj1 = { data: [{ id: 1, name: 'Linda' }, { id: 2, name: 'Mark' } ]},
obj2 = [{ id: 1, salary: "2000, 60 USD" }, undefined, { id: 2, salary: "4000, 50 USD" }, undefined],
map = new Map,
result;
obj1.data.forEach(join);
obj2.forEach(join);
result = { data: Array.from(map.values()) };
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another way
const res = {
...obj1, // take the initial object
data: obj1.data.map(item => ({ // rewrite data property by iterate each item
// and merge item with corresponding
// object from obj2
...item, // take the item object
...obj2.find(({ id }) => id === item.id) // find corresponding object
}))
};
Here is an approach which would combine the objects and not overwrite the properties but only add the ones that are missing as well as avoid the undefined etc:
const names = {data: [{ id: 1, name: 'Linda' },{ id: 2, name: 'Mark' }]}
const salaries = [{ id: 1, salary: "2000, 60 USD" }, undefined]
var result = _.mapValues(names.data, x => {
let hit = _.find(salaries, y => y ? y.id === x.id : null)
return hit ? _.defaults(x, hit) : x
})
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
We are using mapValues to get to the values of names.data and look through them and for each of them get a hit in the salaries. If the hit exists we default the props of the hit with the current data object and return. Hope this helps.

Using template literal for dynamic property in ReactJS

My failed attempt:
temp.map((obj,i) => ({
obj[`person${++i}`] = obj.person.name
})
I want to produce something like this
[{id:324, person1:'mike'},{id:23, person2:'jane'}]
But I'm stuck on making the property dynamic with concatenation using template literal string.
Issue with you code is, you are directly returning the data by using
() => ({....})
and there you are using obj[...] that is not a valid key.
map return the a new array so store the result in a new variable, if you want to modify the same array then better to use forEach.
Check this snippet:
let arr = [{id: 10, name: 'A'}, {id: 20, name: 'B'}];
let newArr = arr.map((el,i) => ({
id: el.id,
[`name${i+1}`]: el.name
}));
console.log('new array', newArr);
Modifying the same data using forEach:
let arr = [{id: 10, name: 'A'}, {id: 20, name: 'B'}];
arr.forEach((el,i) => {
el[`person${i+1}`] = el.name;
})
console.log('modified array', arr);
This should do it:
var myInput = ["John", "Jane", "Steven", "Alice"];
var myOutput = myInput.map ((name, index) => {
var out = {};
out[`person${index}`] = name;
return out;
}); // myOutput is [{person1:"John"}, {person2:"Jane"} ... etc.
map creates a new array rather than modifying the existing one. The values of the array are made out of the return values of the function, so if you want your values to be objects, you must create new objects and then assign your properties to them.
How about this?
describe("sample test", () => {
it("Dynamic property in ES6", () => {
const temp = [
{ id: 324, person: { name: "mike" } },
{ id: 23, person: { name: "jane" } }
];
console.log(
temp.map((obj, i) => ({
id: obj.id,
[`person${i + 1}`]: obj.person.name
}))
);
});
});
Output:
[ { id: 324, person1: 'mike' }, { id: 23, person2: 'jane' } ]

Categories

Resources