Array values not updated properly

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"
      }
    ]
  }
]

>Solution :

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:

  1. for-in isn’t the correct way to loop through an array (usually), see my answer here for full details of your various options.

  2. 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;
}

Leave a Reply