counterbeing
counterbeing

Reputation: 2791

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.

Upvotes: 5

Views: 13882

Answers (3)

Ori Drori
Ori Drori

Reputation: 192287

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

Upvotes: 2

Ari
Ari

Reputation: 1703

@Nenad is correct. You can also do it with reduce and map.

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

const matrix = numbers.reduce((acc, nxt) => {
	const numberedObjs = objects.map(obj => ({...obj, number:nxt}));
	acc.push(...numberedObjs);
	return acc;
}, []);

Upvotes: 0

Nenad Vracar
Nenad Vracar

Reputation: 122087

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)

Upvotes: 8

Related Questions