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

推荐答案

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

Javascript相关问答推荐

Symbol.hasInstance 属性在 JS 中工作

工单命令:DiscordAPIError:交互已被确认

为什么“let = 13”不会在 JavaScript 中抛出错误? (Chrome 控制台 + nodejs)

Svelte 从 JSON 中 Select 特定对象数据

通过判断值是否存在来应用 css

如何向 AG Grid 过滤器添加“营业年度”条件?

在 Javascript 中为 gulpfile.js 舍入 const 变量列表的更好方法?

使用 jQuery 将页面滚动到某个点时,如何更改文本?

Typescript 和 nextjs:SyntaxError: Unexpected token 'export'

用于打印列表中项目的 HTML if else 条件

如何在 Chart.Js 中通过按下按钮(或任何元素)更改 colored颜色 ?

CSS 在不同的设备上以不同的方式显示相同的动画

无法捕获 Node 的内置函数回调引发的错误

如何使列表中的第一项为第二项留出空间,依此类推?

当表格中没有 Select 时禁用继续按钮

React native onChangeText 只执行一个函数

findOne 检索到的对象实例没有任何方法

数据表js:自定义css

react 组件和作为react 组件的功能有什么区别

使用 react-chartjs-2 创建折线图的参考