Update Array containing objects using spread operator - javascript

I have an array containing objects in javascript / typescript.
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}]
How can I update name of the second element (with id 2) and copy the array to a new array using javascript spread (...) operator?

You can use a mix of .map and the ... spread operator
You can set the value after you've created your new array
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {return {...a}})
array2.find(a => a.id == 2).name = "Not Two";
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Or you can do it in the .map
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {
var returnValue = {...a};
if (a.id == 2) {
returnValue.name = "Not Two";
}
return returnValue
})
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Using Spred Operator, you can update particular array value using following method
let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const label = "name";
const newValue = "Two Updated";
// Errow comes if index was string, so make sure it was integer
const index = 1; // second element,
const updatedArray = [
...array.slice(0, index),
{
// here update data value
...array[index],
[label]: newValue,
},
...array.slice(index + 1),
];
console.log(updatedArray);

There are a few ways to do this. I would suggest using Array.map :
let new_array = array.map(element => element.id == 2 ? {...element, name : 'New Name'} : element);
or with Object.assign :
let new_array = array.map(element => element.id == 2 ? Object.assign({}, element, {name : 'New Name'}) : element);
Map returns a new array, so you shouldn't need the array spread operator.

We can use
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = [...array]
array2.find(a => a.id == 2).name = "Not Two";
console.log(array2);

You can simply use map() and change the element there.
here is the code---
array_copy = array.map((element) => {
console.log(element.id);
if (element.id === 2) {
element.name = "name changed";
}
return element;
});
console.log(array_copy);
Here the main array also gets modified, as elements inside the array are objects and it references to the same location even in the new array.

You can do it like this in map, no need for spread:
const array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}]
const updatedArray = array.map(a => {
if (a.id == 2) {
a.name = 'New Name';
}
return a;
});

Merging properties from filterQueryParams to selectedLaws (existing solutions did not suit me):
if (this.filterQueryParams && Object.prototype.toString.call(this.filterQueryParams) === '[object Array]') {
for (const law of this.filterQueryParams) {
if (law as Laws.LawDetail) {
const selectedLaw = this.selectedLaws.find(x => x.languageCode === law.languageCode);
if (selectedLaw) {
for (const propName of Object.keys(law)) {
selectedLaw[propName] = law[propName];
}
}
else {
this.selectedLaws.push(law);
}
}
}
}

import React,{useState} from 'react';
export function App(props) {
const[myObject,setMyObject] = useState({
"Name":"",
"Age":""
});
const[myarray, setmyarray] = useState([]);
const addItem =() =>{
setMyObject({...myObject,"Name":"Da","Age":"20"});
setmyarray([...myarray, 1]);
};
console.log(myarray);console.log(myObject);
return (
<div className='App'>
<h1>Hello React.</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={addItem}>Add me</button>
</div>
);
}
// Log to console
console.log('Hello console')

let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 =[...array.slice(0, 0), Object.assign({}, array[0], {
name:'new one' //change any property of idx
}),...array.slice(0 + 1)]
console.log(array);
console.log(array2);
[...array.slice(0, idx), Object.assign({}, array[idx], {
x:new_x //change any property of idx
}),...array.slice(idx + 1)]

Related

React.js: How to find duplicates for properties in an array of object and put a progressive number on that field

I have an array of object and each object is for example :
const myArr=[{name:"john",id:1}{name:"john",id:2}{name:"mary",id:3}]
for the first 2 element for the property "name" I have the name "john" that is duplicate.
How can I modify the rendered names like that:
const myArr=[{name:"john (1 of 2)",id:1}{name:"john (2 of 2)",id:2}{name:"mary",id:3}]
Thanks in advance!
Reduce the input array into a map by name (i.e. group by name property), and map the array of values to the result array. If the group array has more than 1 element in it then sub-map the group to include the numbering. Flatten the overall result.
const myArr = [
{ name: "john", id: 1 },
{ name: "john", id: 2 },
{ name: "mary", id: 3 }
];
const res = Object.values(
myArr.reduce((groups, current) => {
if (!groups[current.name]) {
groups[current.name] = [];
}
groups[current.name].push(current);
return groups;
}, {})
).flatMap((value) => {
if (value.length > 1) {
return value.map((current, i, arr) => ({
...current,
name: `${current.name} (${i + 1} of ${arr.length})`
}));
}
return value;
});
console.log(res);
You can do use reduce(), filter(), and flat() and do this:
const myArr = [
{name:"john", id:1},
{name:"john", id:2},
{name:"mary", id:3}
]
const res = Object.values(myArr.reduce((acc, curr) => {
const total = myArr.filter(({ name }) => name === curr.name).length;
if(!acc[curr.name]) {
acc[curr.name] = [
{...curr}
]
} else {
const currentSize = acc[curr.name].length;
if(currentSize === 1) {
acc[curr.name][0].name = `${acc[curr.name][0].name} (1 of ${total})`
}
acc[curr.name].push({
...curr,
name: `${curr.name} (${currentSize + 1} of ${total})`
})
}
return acc;
}, {})).flat();
console.log(res);
const myArr = [{name:"john",id:1}, {name:"john",id:2}, {name:"mary",id:3}];
const namesArray = myArr.map(elem => elem.name);
const namesTraversed = [];
let currentCountOfName = 1;
let len = 0;
myArr.forEach(elem => {
len = namesArray.filter(name => name === elem.name).length;
if (len > 1) {
if (namesTraversed.includes(elem.name)) {
namesTraversed.push(elem.name);
currentCountOfName = namesTraversed.filter(name => name === elem.name).length;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
} else {
namesTraversed.push(elem.name);
currentCountOfName = 1;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
}
}
});
console.log(myArr);
Check if this helps you
const myArr = [{
name: "john",
id: 1
}, {
name: "john",
id: 2
}, {
name: "mary",
id: 3
}]
// to keep a track of current copy index
let nameHash = {}
const newMyArr = myArr.map(ele => {
const noOccurence = myArr.filter(obj => obj.name ===ele.name).length;
if(noOccurence > 1){
// if there are multiple occurences get the current index. If undefined take 1 as first copy index.
let currentIndex = nameHash[ele.name] || 1;
const newObj = {
name: `${ele.name} (${currentIndex} of ${noOccurence})`,
id: ele.id
}
nameHash[ele.name] = currentIndex+ 1;
return newObj;
}
return ele;
})
console.log(newMyArr);

Why the original array is being changed even after being cloned with spread operator? [duplicate]

I have an array containing objects in javascript / typescript.
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}]
How can I update name of the second element (with id 2) and copy the array to a new array using javascript spread (...) operator?
You can use a mix of .map and the ... spread operator
You can set the value after you've created your new array
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {return {...a}})
array2.find(a => a.id == 2).name = "Not Two";
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Or you can do it in the .map
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {
var returnValue = {...a};
if (a.id == 2) {
returnValue.name = "Not Two";
}
return returnValue
})
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Spred Operator, you can update particular array value using following method
let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const label = "name";
const newValue = "Two Updated";
// Errow comes if index was string, so make sure it was integer
const index = 1; // second element,
const updatedArray = [
...array.slice(0, index),
{
// here update data value
...array[index],
[label]: newValue,
},
...array.slice(index + 1),
];
console.log(updatedArray);
There are a few ways to do this. I would suggest using Array.map :
let new_array = array.map(element => element.id == 2 ? {...element, name : 'New Name'} : element);
or with Object.assign :
let new_array = array.map(element => element.id == 2 ? Object.assign({}, element, {name : 'New Name'}) : element);
Map returns a new array, so you shouldn't need the array spread operator.
We can use
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = [...array]
array2.find(a => a.id == 2).name = "Not Two";
console.log(array2);
You can simply use map() and change the element there.
here is the code---
array_copy = array.map((element) => {
console.log(element.id);
if (element.id === 2) {
element.name = "name changed";
}
return element;
});
console.log(array_copy);
Here the main array also gets modified, as elements inside the array are objects and it references to the same location even in the new array.
You can do it like this in map, no need for spread:
const array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}]
const updatedArray = array.map(a => {
if (a.id == 2) {
a.name = 'New Name';
}
return a;
});
Merging properties from filterQueryParams to selectedLaws (existing solutions did not suit me):
if (this.filterQueryParams && Object.prototype.toString.call(this.filterQueryParams) === '[object Array]') {
for (const law of this.filterQueryParams) {
if (law as Laws.LawDetail) {
const selectedLaw = this.selectedLaws.find(x => x.languageCode === law.languageCode);
if (selectedLaw) {
for (const propName of Object.keys(law)) {
selectedLaw[propName] = law[propName];
}
}
else {
this.selectedLaws.push(law);
}
}
}
}
import React,{useState} from 'react';
export function App(props) {
const[myObject,setMyObject] = useState({
"Name":"",
"Age":""
});
const[myarray, setmyarray] = useState([]);
const addItem =() =>{
setMyObject({...myObject,"Name":"Da","Age":"20"});
setmyarray([...myarray, 1]);
};
console.log(myarray);console.log(myObject);
return (
<div className='App'>
<h1>Hello React.</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={addItem}>Add me</button>
</div>
);
}
// Log to console
console.log('Hello console')
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 =[...array.slice(0, 0), Object.assign({}, array[0], {
name:'new one' //change any property of idx
}),...array.slice(0 + 1)]
console.log(array);
console.log(array2);
[...array.slice(0, idx), Object.assign({}, array[idx], {
x:new_x //change any property of idx
}),...array.slice(idx + 1)]

How do I make another array a subitem of another array?

I have the following code:
var a = [
{id:16, name:"product",productCount:1},
{id:17, name:"clothes",productCount:1},
]
var b = [
{id:1, parentId:16, name:"phone"},
{id:2, parentId:17, name:"coat"},
{id:3, parentId:16, name:"mac"},
{id:4, parentId:16, name:"apple"},
]
I tried the following:
According to the condition,Let b be a subarray of a
let result = []
a.forEach( ( item ,index)=>{
if(b[index].parentId === item.id){
result.push(item);
result[index].childAs=[];
result[index].childAs.push(b[index]);
}
})
I don't get what I want,
I expect results:
var a = [
{id:16, name:"product",productCount:1, childAs:[
{id:1, parentId:16, name:"phone"},
{id:3 ,parentId:16, name:"mac"},
{id:4, parentId:16, name:"apple"},
]
},
{id:17, name:"clothes",productCount:1, childAs:[{id:2, parentId:17, name:"coat"}]}
]
Update desired results:
What should I do if I change the result to this?
var result =
[
{label:'a name value',value:"a id value",
children:[{label:"b name value",value:"b parentId value"}]
},
...
]
Can you help me?
You can use forEach with find - for each item in b, check if the parentId exists as an id in a. If there's an item check if there's a children prop. If there is, push the element to it, if there isn't, create a new array with the element. If there is no item, carry on the loop:
var a = [
{id:16, name:"product",productCount:1},
{id:17, name:"clothes",productCount:1},
]
var b = [
{id:1, parentId:16, name:"phone"},
{id:2, parentId:17, name:"coat"},
{id:3, parentId:16, name:"mac"},
{id:4, parentId:16, name:"apple"},
]
b.forEach(o => {
const item = a.find(({id}) => id === o.parentId)
item && item.children ? item.children.push(o) : item.children = [o]
})
console.log(a)
A more efficient way to write this would be to construct a map first, instead of calling find on every loop:
var a = [{id:16, name:"product",productCount:1},{id:17, name:"clothes",productCount:1}]
var b = [{id:1, parentId:16, name:"phone"},{id:2, parentId:17, name:"coat"},{id:3, parentId:16, name:"mac"},{id:4, parentId:16, name:"apple"}]
const map = new Map(a.map(({id}, i) => [id, i]))
b.forEach(o => {
const i = map.get(o.parentId)
i !== undefined && a[i].children ? a[i].children.push(o) : a[i].children = [o]
})
console.log(a)
We use reduce to create a new array.
We construct new objects by taking the current object from array a and adding a new key to it. The new key is the result of applying filter to array 'b', where we selected the ones that match our current object's id (from array 'a').
var a = [{id:16,name:"product",productCount:1},{id:17,name:"clothes",productCount:1},]
var b = [{id:1,parentId:16,name:"phone"},{id:2,parentId:17,name:"coat"},{id:3,parentId:16,name:"mac"},{id:4,parentId:16,name:"apple"},];
function childToParentMerge(ar1, ar2) {
return ar1.reduce((a,c) => (a.push({...c, childAs: ar2.filter(o => o.parentId == c.id)}),a),[])
}
console.log(childToParentMerge(a,b))
The following is conform with the updated description:
var a = [{id:16,name:"product",productCount:1},{id:17,name:"clothes",productCount:1},]
var b = [{id:1,parentId:16,name:"phone"},{id:2,parentId:17,name:"coat"},{id:3,parentId:16,name:"mac"},{id:4,parentId:16,name:"apple"},];
function childToParentMerge(ar1, ar2) {
return ar1.reduce((a,c) => (a.push({label: c.name, value: c.id, childAs:
ar2.reduce((ac,oc) =>
(oc.parentId == c.id && ac.push({label: oc.name, value: oc.parentId}),ac),[])
}),a),[])
}
console.log(childToParentMerge(a,b))
You can use map and filter
map over array a, for each element filter the values form b with parentId same as id of current element
Build a new object with current element along with the childAs property
var a = [{id:16, name:"product",productCount:1},{id:17, name:"clothes",productCount:1}]
var b = [{id:1, parentId:16, name:"phone"},{id:2, parentId:17, name:"coat"},{id:3, parentId:16, name:"mac"},{id:4, parentId:16, name:"apple"}]
let final = a.map(inp => {
let childs = b.filter(({parentId})=> parentId === inp.id)
return {...inp,childAs:childs}
})
console.log(Object.values(final))
You could take a Map and push the values to the corresponding parents.
var a = [{ id: 16, name: "product", productCount: 1 }, { id: 17, name: "clothes", productCount: 1 }],
b = [{ id: 1, parentId: 16, name: "phone" }, { id: 2, parentId: 17, name: "coat" }, { id: 3, parentId: 16, name: "mac" }, { id: 4, parentId: 16, name: "apple" }],
map = a.reduce((m, o) => m.set(o.id, Object.assign({}, o, { children: [] })), new Map),
result = [...map.values()];
b.forEach(o => map.get(o.parentId).children.push(o));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could group b based on parentId. Then map the a array and get the children from grouped
const a=[{id:16,name:"product",productCount:1},{id:17,name:"clothes",productCount:1},],
b=[{id:1,parentId:16,name:"phone"},{id:2,parentId:17,name:"coat"},{id:3,parentId:16,name:"mac"},{id:4,parentId:16,name:"apple"},];
const grouped = b.reduce((map, o) =>
map.set(o.parentId, (map.get(o.parentId) || []).concat(o))
, new Map)
const output = a.map(o => ({ ...o, childAs: grouped.get(o.id) || [] }))
console.log(output)

How to transform Array to Object?

What is the best way to transform an array like this:
const arr = [
{ name: 'Bob' },
{ name: 'Ben' }
{ name: 'Cole' }
{ name: 'Mary' }
{ name: 'Travis' }
]
to an object like:
const obj = {
'B': ['Bob', 'Ben'],
'C': ['Cole'],
'M': ['Mary'],
'T': ['Travis']
}
Using only vanilla JS
You can use array#reduce. Iterate through each object of your array and then extract out the first letter and add names corresponding to it.
const arr = [{name: 'Bob'}, {name: 'Ben'}, {name: 'Cole'}, {name: 'Mary'}, {name: 'Travis'}],
result = arr.reduce((r,{name}) => {
r[name[0]] = r[name[0]] || [];
r[name[0]].push(name);
return r;
},{});
console.log(result);
Vanilla JS you say? Here you go
let nil = x => x === undefined;
let empty = ([h]) => nil(h);
let first = ([h]) => h;
let last = ([h, ...t]) => empty(t) ? h : last(t);
let map = ([h, ...t], f) => nil(h) ? [] : [f(h), ...map(t, f)];
let reduce = ([h, ...t], f, i) => nil(h) ? i : reduce(t, f, f(i, h));
let tab = (a, f) => map(a, x => [x, f(x)]);
let push = (a, x) => nil(a) ? [x] : [...a, x];
let groupBy = (a, f) => _groupBy(tab(a, f));
let _groupBy = ka => reduce(ka, (g, [x, k]) => ({...g, [k]: push(g[k], x)}), {});
///
const arr = [{ name: 'Bob' },{ name: 'Ben' },{ name: 'Cole' },{ name: 'Mary' },{ name: 'Travis' }]
z = groupBy(map(arr, x => x.name), first)
console.log(z)
No built-ins!
I created an array where the key is the first letter of the name using the reduce function and restructuring the 'name' from the objects. If the key exists in the array the name is pushed (using spread operator). Else, it creates the key with only one element.
const arr = [
{ name: 'Bob' },
{ name: 'Ben' },
{ name: 'Cole' },
{ name: 'Mary' },
{ name: 'Travis' }
];
const obj = arr.reduce((res, {name})=>{
res[name[0]] = res[name[0]] ? [...res[name[0]],name] : [name];
return res;
}, {});
console.log(obj);
I think this thread is missing a non functional answer, so here it is:
const obj = {};
for(const {name} of arr)
(obj[name[0]] || (obj[name[0]] = [])).push({name});
let obj = {};
arr.forEach( e => {
const name = e.name;
if (!obj[name.charAt(0)]) obj[name.charAt(0)] = [];
obj[name.charAt(0)].push(name);
})
I'm generating a new object and adding to it news keys based in the first char of the name values (only if the key hasn't been already added).
Then, I add each value to the key that corresponds.

filter array of object base on array of object

This is my data
[{name:'james',grade:'A'},
{name:'john',grade:'B'},
{name:'iris',,grade:'A'},
{name:'ivan',,grade:'C'}]
I want to keep object that has grade A and C, it's easy I can just do filter like
person.filter(obj => obj.grade === 'A' || obj.grade === 'C')
but now I have an array of object.
[{grade:'A'},{grade:'C'}]
any clue how can I do filtering now? do I need nested loop?
Use Array.prototype.some:
let person = [{name:'james', grade:'A'},
{name:'john', grade:'B'},
{name:'iris', grade:'A'},
{name:'ivan', grade:'C'}];
let predicate = [{grade:'A'},{grade:'C'}];
let result = person.filter(obj => predicate.some(p => p.grade == obj.grade))
console.log('result:', result)
If your predicate is more dynamic than that, compare all object properties instead of just p.grade.
person.filter(obj => predicate.some(p => {
return Object.keys(p).every(k => obj[k] == p[k]);
}));
Using underscore lib
eg -
var bbb = [
{id: 839},
{id: 854}
];
var ids = {};
_.each(bbb, function (bb) { ids[bb.id] = true; });
var data = [{grade:'A'},{grade:'C'}];
var value = {};
_.each(data , function (d) { value[data.garde] === 'A' | value[data.garde] === 'C' ; });
Objects having grades A and C should be filtered as (classic js syntax),
var a = [
{name:'james',grade:'A'},
{name:'john',grade:'B'},
{name:'iris',grade:'A'},
{name:'ivan',grade:'C'}
];
a.filter(function(e) {
return (e.grade == 'A') || (e.grade == 'C');
});
If you have many grades to check (I don't know like all of them ;) ). You could first convert array into Set
const grades = new Set([{grade:'A'},{grade:'C'}].map(({grade}) => grade))
const persons = [{name:'james',grade:'A'},
{name:'john',grade:'B'},
{name:'iris',grade:'A'},
{name:'ivan',grade:'C'}]
And then filter persons array using has
const filtered = persons.filter(({grade}) => grades.has(grade))
You could use a Set for the predicates and filter accordingly
let person = [{ name: 'james', grade: 'A' }, { name: 'john', grade: 'B' }, { name: 'iris', grade: 'A' }, { name: 'ivan', grade: 'C' }],
predicate = [{ grade: 'A' }, { grade: 'C' }],
result = person.filter((s => p => s.has(p.grade))(new Set(predicate.map(p => p.grade))));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources