-3

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

solimanware
  • 2,952
  • 4
  • 20
  • 40
  • 2
    It's absolutely not _"weird"_. It's working as expected because [`.add()`](https://momentjs.com/docs/#/manipulating/add/) _"mutates the original moment by adding time."_ – Andreas Dec 18 '19 at 15:45
  • @Andreas but I am adding 0 hours at first iteration 8 at second iteration and 16 at last iteration why the array is resulting in same value? – solimanware Dec 18 '19 at 15:46
  • I expect all dates to be equal actually, the value of the last mutation. Every iteration is mutating the same instance – Jeffrey Devloo Dec 18 '19 at 15:46
  • but why second loop working normally? – solimanware Dec 18 '19 at 15:48
  • The second loop appears to work normally as it output at each interval instead of at the end. It should still be startdate +0 at first + 0 + 8 at second and + 0 + 8 + 16 at the end – Jeffrey Devloo Dec 18 '19 at 15:50
  • You're adding `0+8+16+0+8+16` hours to the initial value of `today`. After the loop `today` should be two days in the future. – Andreas Dec 18 '19 at 15:50
  • Is JavaScript too fast in creating the array so the first loop is not working but the second is working? – solimanware Dec 18 '19 at 15:54
  • How Array.push is working? – solimanware Dec 18 '19 at 15:57
  • It's mutating the same value in first loop but has different behaviour working normally in second loop – solimanware Dec 18 '19 at 15:59
  • the second loop isn't "working"... console.log is calling the to string method of the moment object AT THAT POINT IN TIME to log. you're still mutating the same object over and over but it's not affecting past logged string representations of the object. if you were calling `timesPerDay.push(today.add(8 * i, "hour").toString());` on each iteration, you'd see the same results in the first loop. – bryan60 Dec 18 '19 at 16:01
  • Here is what you get if you output it on the loops: 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. It "appears" to be correct. But if you actually count the offset, you notice it isn't. – Jeffrey Devloo Dec 18 '19 at 16:03
  • I still don't get it :( – solimanware Dec 18 '19 at 16:07
  • Still not working after I cloned new object: https://codepen.io/microsmsm-the-flexboxer/pen/KKwaoeM – solimanware Dec 18 '19 at 16:28
  • Your result is correct now. Focus on the string output instead of the properties of the object. You can get the string representation by .toString() or .format... – Jeffrey Devloo Dec 18 '19 at 16:33

3 Answers3

1

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

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
bryan60
  • 28,215
  • 4
  • 48
  • 65
0

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)
Jeffrey Devloo
  • 1,406
  • 1
  • 11
  • 20
  • The answer is not working for me I tried "cloning the object" but still have same results in array – solimanware Dec 18 '19 at 16:10
  • The answer is working. I've added the same console logging as the 'proof'. The screenshot provided is code which differs from my answer. Update your snippet instead of a screenshot. – Jeffrey Devloo Dec 18 '19 at 16:17
  • It is the result that you want, no? If you stringify the date, you get what you expected. I think you are focussing on the _i property which is the initial date, whilest the _d property is the modified one. What the _i and _d do, is beyond me, but when you stringify the dates, the correct output is generated. – Jeffrey Devloo Dec 18 '19 at 16:31
  • Clarification of _i and _id can be found [here](https://stackoverflow.com/questions/28126529/momentjs-internal-object-what-is-d-vs-i) – Jeffrey Devloo Dec 18 '19 at 16:34