Search code examples
javascriptnode.jsasynchronousforeachobject-notation

JavaScript Object w/Async + forEach Loop - problems with value assignment


My goal here is to create an object, 'tallys,' that has a key for each user in a game, and the value assigned to those user keys is an array representing weekly dollar values won in a game. To begin, I assign each user in the game an array of length 26 (representing 26 weeks in the game) with 26 values of '0' to represent $0 values for each week to begin with via this code:

    const maxWeek = 25;
    let weeks = [];
    for (let i=0; i < maxWeek+1; i++) {
      weeks.push(0)
    };
    let tallys = {};
    users.forEach(user => {
      tallys[user] = weeks;
    });

...which results in an object like so:

tallys is  {
  michaeljpow: [
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0
  ],
  'Mr. Cheeseburger': [
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0
  ],
  'brewster.stanislaw': [
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0
  ],
  ... etc
}

Then, I run a little for loop to calculate the totals for each user by week ... with the 'j' variable here starting at week 1, and the loop running for as many weeks as the game has been running, like this:

for (let j=1; j<wk+1; j++) {
  let sums = await BonusEvents().where({week: j}).sum('dollars_total as d').groupBy('username').select('username');
  console.log('sums for week ', j, ' are ', sums);
 };

I'm still in good shape at this point -- this gets me what I want, two different 'sums' arrays for the two weeks that have taken place in the game so far:

sums for week 1 are  [
  { d: -4.27, username: 'Mr. Cheeseburger' },
  { d: -4.27, username: 'dannuzio' },
  { d: 11.29, username: 'james.johnsonf' },
  { d: -4.27, username: 'brewster.stanislaw' },
  { d: 16.47, username: 'eric.wj.clarke' },
  { d: -4.27, username: 'mikeduin' },
  { d: 11.29, username: 'BH84' },
  { d: -4.27, username: 'kevsims' },
  { d: 11.29, username: 'michaeljpow' },
  { d: 47.58, username: 'superbkn' },
  { d: -4.27, username: 'whpfwashington' },
  { d: -4.27, username: 'benjamininghram' }
]
sums for week 2 are  [
  { d: -1.41, username: 'benjamininghram' },
  { d: -1.41, username: 'BH84' },
  { d: -1.41, username: 'brewster.stanislaw' },
  { d: -1.41, username: 'dannuzio' },
  { d: -1.41, username: 'eric.wj.clarke' },
  { d: -1.41, username: 'james.johnsonf' },
  { d: -1.41, username: 'kevsims' },
  { d: -1.41, username: 'michaeljpow' },
  { d: -1.41, username: 'mikeduin' },
  { d: -1.41, username: 'Mr. Cheeseburger' },
  { d: 14.06, username: 'superbkn' },
  { d: -1.41, username: 'whpfwashington' },
]

So here's where things get messy. I modify the previous function to do a forEach on my 'sums' array for each week, with designs on modifying the 'tallys' object to modify the array position assigned to each week with the appropriate dollar value for each user. (Also, lest the 'j-1' here look confusing -- that's because I want the game week of 1 assigned to the 0 position in the array rather than the first position):

  for (let j=1; j<wk+1; j++) {
      let sums = await BonusEvents().where({week: j}).sum('dollars_total as d').groupBy('username').select('username');
      sums.forEach(weekSum => {
        tallys[weekSum.username][j-1] = weekSum.d;
       console.log('tallys are ', tallys);
      })
    };

So what I would EXPECT here is to end up with something like this:

tallys = {
mikeduin: [-4.27, -1.41, 0, 0, 0 ... etc],
superbkn: [47.58, 14.06, 0, 0, 0 ... etc],
... etc
}

... with the appropriate values for each user. HOWEVER! This is what I end up with as my tallys object once all the loops etc have finished:

tallys are  {
  michaeljpow: [
    -4.27, -1.41, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0
  ],
  'Mr. Cheeseburger': [
    -4.27, -1.41, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0
  ],
  superbkn: [
    -4.27, -1.41, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0, 0, 0, 0, 0,
        0,     0
  ],
   ...etc
}

.... etc, but for every single user. It's assigning EVERY user the exact same value over and over again -- they all end up with -4.27 and -1.41 just because that was the last 'd' value that got looped through.

Why is it updating every username with the last relevant values when I have tallys[weekSum.username] in my code, so that it should only be updating a single username each time in the tallys object??

Thanks in advance for any help!


EDIT: Thank you again for everyone's help. @Bergi's answer in the comments is correct. When I edit my initial code that creates 'tallys' as he suggests like so -- everything works as it should. @vothanhdat's approach that I marked as the suggested answer in which I clone the initial 'weeks' array also solves the problem.

let tallys = {};
users.forEach(user => {
  let weeks = [];
  for (let i=0; i < maxWeek+1; i++) {
     weeks.push(0)
  };
  tallys[user] = weeks;
});

Solution

  • Your problem is every tallys[user] reference to the same weeks array.

    Example:

    let a = [1,2,3]
    console.log(a) // [1,2,3]
    let b = a // now a & b is reference to the same array
    b[0] = 100 
    console.log(a) // [100,2,3]
    

    So the solution is clone array week for every user

    const maxWeek = 25;
    let weeks = [];
    for (let i = 0; i < maxWeek + 1; i++) {
      weeks.push(0)
    };
    let tallys = {};
    users.forEach(user => {
      tallys[user] = [...weeks] //Clone week array
    });