Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using nested forEach loops to push and object to an array

I'd love an explanation as to why the result of this function don't match my expectation.

const numbers = [ 1, 2 ]
const objects = [{f: 'a'}, {f: 'b'}]

async function(domain) {
  let matrix = []
  objects.forEach((object) => {
    numbers.forEach((number) => {
      object.number = number
      matrix.push(object)
    })
  }) 
  return matrix
}()

Actual

The result, once the promise is resolved returns:

[ 
  { f: 'a', number: 2 },
  { f: 'a', number: 2 },
  { f: 'b', number: 2 },
  { f: 'b', number: 2 } 
] 

Expected

But, my expectation is that it would return:

[ 
  { f: 'a', number: 1 },
  { f: 'a', number: 2 },
  { f: 'b', number: 1 },
  { f: 'b', number: 2 } 
] 

One of the things that puzzles me the most is that if I console.log the value of object just before I push, it logs out each of the objects in my expected result.

like image 212
counterbeing Avatar asked Oct 07 '17 18:10

counterbeing


2 Answers

Because objects are passed by reference and you are modifying original object, instead you can push new object.

const numbers = [ 1, 2 ]
const objects = [{f: 'a'}, {f: 'b'}]

let matrix = []
objects.forEach((object) => {
  numbers.forEach((number) => {
    matrix.push({...object, number})
  })
})

console.log(matrix)
like image 136
Nenad Vracar Avatar answered Oct 21 '22 05:10

Nenad Vracar


Use Array.flatMap() to iterate the objects, with a nested Array.map() to iterate the numbers and return an array of new objects with the relevant number. The flatMap will flatten the arrays produced by the map to a single array:

const numbers = [ 1, 2 ]
const objects = [{f: 'a'}, {f: 'b'}]

const matrix = objects.flatMap(o => // map the objects and flatten
  numbers.map( // map the numbers
    number => ({ ...o, number }) // return a new object with the number
  )
);

console.log(matrix);

Old answer:

Use nested Array#map to iterate the objects and the numbers, and Object#assign to create new objects that include the relevant number. Flatten resulting nested arrays by using the spread syntax in Array#concat:

const numbers = [ 1, 2 ]
const objects = [{f: 'a'}, {f: 'b'}]

const matrix = [].concat(... // flatten the nested arrays
  objects.map((o) => // map the objects
    numbers.map( // map the numbers
      (number) => Object.assign({}, o, { number }) // return a new object with the number
    )
  ));

console.log(matrix);
like image 31
Ori Drori Avatar answered Oct 21 '22 05:10

Ori Drori