Reputation: 121
I'm having an issue where I want to use an existing array and update one of it's properties using a for loop. I expect the outer id and the id inside the array to match, so for example: a & a, b & b, c & c. However when I print out my final array, all of the inner ids are set to c.
I don't understand why this happens?
edit: added expected output
My Code
const accounts = [{
"id": 1,
"currency": "USD"
}];
const alphaId = ['a', 'b', 'c'];
let finalArr = [];
for (index in alphaId) {
let newAccount = {};
newAccount.id = alphaId[index];
newAccount.accounts = accounts;
newAccount.accounts.forEach(acc => {
acc.id = alphaId[index];
});
finalArr.push(newAccount);
}
Print Out
[
{
"id": "a",
"accounts": [
{
"id": "c",
"currency": "USD"
}
]
},
{
"id": "b",
"accounts": [
{
"id": "c",
"currency": "USD"
}
]
},
{
"id": "c",
"accounts": [
{
"id": "c",
"currency": "USD"
}
]
}
]
Expected Output
[
{
"id": "a",
"accounts": [
{
"id": "a",
"currency": "USD"
}
]
},
{
"id": "b",
"accounts": [
{
"id": "b",
"currency": "USD"
}
]
},
{
"id": "c",
"accounts": [
{
"id": "c",
"currency": "USD"
}
]
}
]
Upvotes: 2
Views: 60
Reputation: 1074335
The problem is here:
newAccount.accounts = accounts;
That just makes the property accounts
on the new object refer to the same array as accounts
, it doesn't copy the array. So all your code is working on the same array, and on the same object within that array.
I suspect you meant to copy both the array and the objects in it. We can do that with map
, having it return new objects.
newAccount.accounts = accounts.map((account) => ({...account}));
That uses map
, spread syntax (it's not an operator), and arrow functions. It's using the concise form of an arrow function (no {
after the arrow) where the body is just an expression. Since what we're returning is an object literal, which starts with {
, we have to wrap the entire body in ()
so the parser realizes we're using the concise form. Here's what it would look like if we used the "verbose" form:
newAccount.accounts = accounts.map((account) => {
return {...account};
});
Here's the minimal updated version, but keep reading:
const accounts = [
{
id: 1,
currency: "USD",
},
];
const alphaId = ["a", "b", "c"];
let finalArr = [];
for (index in alphaId) {
let newAccount = {};
newAccount.id = alphaId[index];
newAccount.accounts = accounts.map((account) => ({...account}));
newAccount.accounts.forEach((acc) => {
acc.id = alphaId[index];
});
finalArr.push(newAccount);
}
console.log(finalArr);
.as-console-wrapper {
max-height: 100% !important;
}
That loops through accounts
twice: once to make the copy, and then again to assign the id
. We can do it just once by assigning the id
while making the copy, using map
:
newAccount.accounts = accounts.map((account) => ({...account, id: alphaId[index]}));
const accounts = [
{
id: 1,
currency: "USD",
},
];
const alphaId = ["a", "b", "c"];
let finalArr = [];
for (index in alphaId) {
let newAccount = {};
newAccount.id = alphaId[index];
newAccount.accounts = accounts.map((account) => ({...account, id: alphaId[index]}));
finalArr.push(newAccount);
}
console.log(finalArr);
.as-console-wrapper {
max-height: 100% !important;
}
Some other notes:
for-in
isn't the correct way to loop through an array (usually), see my answer here for full details of your various options.
Since finalArr
is a mapping of alphaId
's ID values to objects, we can use map
instead of the loop, handling creating the new accounts within the map
:
Example:
const finalArr = alphaId.map((id) => ({
id,
accounts: accounts.map((account) => ({...account, id})),
}));
const accounts = [
{
id: 1,
currency: "USD",
},
];
const alphaId = ["a", "b", "c"];
const finalArr = alphaId.map((id) => ({
id,
accounts: accounts.map((account) => ({...account, id})),
}));
console.log(finalArr);
.as-console-wrapper {
max-height: 100% !important;
}
Upvotes: 2