Array Push 'Different Info' Results in Pushing Same First Iteration For Every Iteration

242 Views Asked by At

I am trying to notify user about his medication 3 times daily So I created:

let timesPerDay = []

const today = moment();
//not working
for (let i = 0; i < 3; i++) {
  timesPerDay.push(today.add(8 * i, "hour"));
}
//working normally
for (let i = 0; i < 3; i++) {
  console.log(today.add(8 * i, "hour"));
}

console.log(timesPerDay)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

But there is a weird bug that when pushing to array it saves the first date for every iteration not adding 8 hours to it every time, that's so weird!

How Array.push is working?

Is JavaScript too fast in creating the array so the first loop is not working but the second is working?

CodePen: https://codepen.io/microsmsm-the-flexboxer/pen/BaypYVO

Edit:

Same Snipped I am using after answers is not working

https://codepen.io/microsmsm-the-flexboxer/pen/KKwaoeM

enter image description here

enter image description here

3

There are 3 best solutions below

7
On BEST ANSWER

walking you through your code step by step:

let timesPerDay = [] // create an array

const today = moment(); // create a moment object

for (let i = 0; i < 3; i++) {
  // add hours to moment object
  // push a reference to that moment object into your array
  // it's always a reference to the SAME moment object, so the same object is referenced in your array multiple times
  // mutations to that object will show everywhere it is referenced
  timesPerDay.push(today.add(8 * i, "hour")); 
}

for (let i = 0; i < 3; i++) {
  // add hours to same moment object
  // log the string representation of that object AT EACH ITERATION
  // once transformed to a string, the string representation will not change as it is no longer part of the object
  console.log(today.add(8 * i, "hour"));
}

console.log(timesPerDay) // log string representation of your array, which is just 3 references to the same object
1
On

this happens because you use same moment object to push So this should help

let timesPerDay = []

for (let i = 0; i < 3; i++) {
  timesPerDay.push(moment().add(8*i,"hour"));
}

for (let i = 0; i < 3; i++) {
  console.log(moment().add(8*i,"hour"));
}

console.log(timesPerDay)

4
On

Since momentjs .add is mutating the today variable, all dates will be the same (equal to the the starting date + 0 + 8 + 16 hours) because you are pushing the same reference all the time.

You have to clone the date to start fresh.

const today = moment()
console.log(`Today: ${today}`)
// Today: Wed Dec 18 2019 17:14:11 GMT+0100
// Loop 0: Wed Dec 18 2019 17:14:11 GMT+0100
// Loop 1: Thu Dec 19 2019 01:14:11 GMT+0100
// Loop 2: Thu Dec 19 2019 09:14:11 GMT+0100
const times = [0, 1, 2].map(offset => {
    const date = moment(today) // clone
    date.add(8*offset,"hour")
    console.log(`Loop ${offset}: ${date}`)
    return date
})

Edit: not + 8 + 16 + 24 but 0 + 8 + 16

Edit 2: Proof that the second iteration with the console.log is not outputting what OP wants:

let timesPerDay = []

const today = moment();
console.log(`today: ${today}`)

// today: Wed Dec 18 2019 17:03:02 GMT+0100
// Loop 0: Wed Dec 18 2019 17:03:02 GMT+0100
// Loop 1: Thu Dec 19 2019 01:03:02 GMT+0100
// Loop 2: Thu Dec 19 2019 17:03:02 GMT+0100
// Last loop is 24 hours later than the initial date!
for (let i = 0; i < 3; i++) {
  // Mutates your start date
   const newDate = today.add(8*i,"hour");
   timesPerDay.push(newDate);
   console.log(`Loop ${i}: ${newDate}`);
}

console.log(timesPerDay)