Array.push () always stacking the last forEach item

I am working on the development of a dynamic table and I am facing problems to create the rows of this table according to the columns I have and the information I receive from the API.

Considering the following code:

let data = [];
let line = {};

const columns = [{
  name: "Code",
  dataIndex: "id"
}, {
  name: "Name",
  dataIndex: "name"
}];

const arrFromApi = [{
  id: 1,
  name: "Marcus",
  age: "32"
}, {
  id: 2,
  name: "John",
  age: "30"
}, {
  id: 3,
  name: "Emily",
  age: "25"
}];

arrFromApi.forEach(item => {
  columns.forEach(column => {
    line[column.dataIndex] = item[column.dataIndex];
  })
  data.push(line)
});

console.log(data)

I have the following result:

[{id: 3, name: "Emily"}, {id: 3, name: "Emily"}, {id: 3, name: "Emily"}]

However, if I replace my data.push(line) with console.log(line) it is printed in the console correctly each of the names the way I need:

[{id: 1, name: "Marcus"}, {id: 2, name: "John"}, {id: 3, name: "Emily"}]

Would anyone know say what's wrong with this scenario? Because I understand that if the console is printing in this sequence, then the final result of my array should be the same.

Author: hkotsubo, 2019-08-25

2 answers

What happens is that with each iteration you are overwriting the id and name of the line object.

The correct would be to create a new object every iteration:

let data = [];

const columns = [{
  name: "Code",
  dataIndex: "id"
}, {
  name: "Name",
  dataIndex: "name"
}];

const arrFromApi = [{
  id: 1,
  name: "Marcus",
  age: "32"
}, {
  id: 2,
  name: "John",
  age: "30"
}, {
  id: 3,
  name: "Emily",
  age: "25"
}];

arrFromApi.forEach(item => {
  let line = {}; // <--- Aqui
  columns.forEach(column => {
    line[column.dataIndex] = item[column.dataIndex];
  })
  data.push(line)
});

console.log(data)

The console.log only "works" because it shows the data you just set to line. But since it is the same object in all iterations, in the end you end up with 3 copies of it in the array data. By creating a new object with each iteration, you eliminate this problem.

 5
Author: hkotsubo, 2019-08-25 21:24:46

Another Way is by converting the object line to string each iteration of forEach using JSON.stringify() and parsing with JSON.parse():

data.push(JSON.parse(JSON.stringify(line)));

With this you will not be adding the same object in the array data, but an object with value independent of the object line.

What you're doing is basically this:

data[line, line, line]

Where the variable line is an object that at the end of forEach will have as its value the last object of the array arrFromApi, i.e. {"id":3,"name":"Emily"}, as said in the answer of hkotsubo.

In this case, you can keep your code as it is, just by changing the line of push as mentioned above:

let data = [];
let line = {};

const columns = [{
  name: "Code",
  dataIndex: "id"
}, {
  name: "Name",
  dataIndex: "name"
}];

const arrFromApi = [{
  id: 1,
  name: "Marcus",
  age: "32"
}, {
  id: 2,
  name: "John",
  age: "30"
}, {
  id: 3,
  name: "Emily",
  age: "25"
}];

arrFromApi.forEach(item => {
  columns.forEach(column => {
    line[column.dataIndex] = item[column.dataIndex];
  })
  data.push(JSON.parse(JSON.stringify(line)));
});

console.log(data)
 1
Author: Sam, 2019-08-26 11:27:17