for inside another for is executed just once - javascript

I have the following code to create all possible intervals between two dates:
var minStart = new Date(2019, 10, 1);
var maxStart = new Date(2019, 10, 3);
var minStop = new Date(2019, 10, 20);
var maxStop = new Date(2019, 10, 22);
for (var i = minStart; i <= maxStart; i.setDate(i.getDate() + 1)) {
for (var v = minStop; v <= maxStop; v.setDate(v.getDate() + 1)) {
console.log(moment(i).format('DD/MM') + ' - ' + moment(v).format('DD/MM'));
}
}
I am expecting to get the following result:
01/11 - 20/11
01/11 - 21/11
01/11 - 22/11
02/11 - 20/11
02/11 - 21/11
02/11 - 22/11
03/11 - 20/11
03/11 - 21/11
03/11 - 22/11
but I am getting only:
>01/11 - 20/11
>01/11 - 21/11
>01/11 - 22/11
I debugged the code by putting more console.log() outputs and it turns out, that the inner loop is run only once. Any idea why this is happening?
Here is a quick JSFiddle (without the moment library that I am using only for formatting).

The problem is that you're mutating the objects, at the end of the first outer loop, minStop will have the same date as maxStop. To address that, use something like this: var v = new Date(minStop)

Related

Why did I get different results? What's different between two codes?

it's my first question on this website. haha.
I was trying to make some dummy data which is from yesterday 12AM to now 15 minutes apart.
here is my code.(Javascript)
const toDay = new Date();
const fromDay = new Date(
toDay.getFullYear(),
toDay.getMonth(),
toDay.getDate() - 1,
0,
0
);
const duration = Math.floor((toDay - fromDay) / (1000 * 60 * 15));
console.log('duration', duration);
let arrayOfData = [];
let dataForX = fromDay;
let dataForY = 0;
for (let i = 0; i < duration; i++) {
arrayOfData.push({
date: dataForX,
value: dataForY,
});
// dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));
dataForX.setMinutes(dataForX.getMinutes() + 15);
dataForY = Math.random() * 100;
}
console.log('arrayOfData', arrayOfData);
I wanted some data like Pic1 to make:
But, it resulted in Pic2:
Fortunately, I solve this problem by coding like this,
dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));
instead of this.
dataForX.setMinutes(dataForX.getMinutes() + 15);
But, I still don't understand why it came this different result out.
Can you tell me the reason for this result?
When you write dataForX.setMinutes(dataForX.getMinutes() + 15); you are actually changing the reference of the same object, thus all dates end up being the same.
When you write dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));, you create a new object(reference) every time. The name of the variable is the same, but the reference changes. You store the old reference first, then create a new one.
Consider the below snippet. The principle is the same as with the dates, but I created an object to make it easier to understand.
When I change foo to be foo = {bar:10};, you might think that the value inside the array would be changed to 10, but it isn't. We are creating a new reference, so the old reference is not affected by our change.
const myArray = [];
let foo = {bar: 3};
myArray.push(foo);
console.log(myArray); // will print 3
foo.bar = 5;
console.log(myArray); // will print 5
foo = {bar:10}; // creating a new reference using the same variable name
console.log(myArray); // will still print 5
myArray.push(foo);
console.log(myArray); // will print 5 and 10
foo.bar = 12;
console.log(myArray); // will print 5 and 12
You need a copy of the date object:
arrayOfData.push({
date: new Date(dataForX.getTime()), // <-- Here
value: dataForY,
});
Example with the change:
const toDay = new Date();
const fromDay = new Date(
toDay.getFullYear(),
toDay.getMonth(),
toDay.getDate() - 1,
0,
0
);
const duration = Math.floor((toDay - fromDay) / (1000 * 60 * 15));
console.log('🚀 ~ file: script.js ~ line 10 ~ duration', duration);
let arrayOfData = [];
let dataForX = fromDay;
let dataForY = 0;
for (let i = 0; i < duration; i++) {
arrayOfData.push({
date: new Date(dataForX.getTime()),
value: dataForY,
});
// dataForX = new Date(dataForX.setMinutes(dataForX.getMinutes() + 15));
dataForX.setMinutes(dataForX.getMinutes() + 15);
dataForY = Math.random() * 100;
}
console.log('arrayOfData', arrayOfData);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Javascript date has two different values depending on in what context it is printed

I'm seeing some weirdness where a Date-object has one value when outputted as part of an object, as compared to when it is outputted by itself. The following code-snippet plus output probably best explains the issue I'm having. Of specific interest is $scope.firstDateOfWeek.
$scope.month = $scope.month - 1;
$scope.firstDateOfMonth = new Date($scope.year, $scope.month);
$scope.firstDay = $scope.firstDateOfMonth.getDay();
$scope.firstDateOfWeek = new Date($scope.year, $scope.month, 2 - $scope.firstDay);
$scope.lastDate = new Date(new Date($scope.year, $scope.month + 1) - 1);
console.log($scope);
console.log($scope.firstDateOfWeek);
console.log($scope);
console.log($scope.firstDateOfWeek);
console.log($scope);
console.log($scope.firstDateOfWeek);
Output (the same x 3):
To be specific, when I do console.log($scope), then $scope.firstDateOfWeek get's printed out as 2015-02-02, but when I explicitly print the variable, console.log($scope.firstDateOfWeek), then it gets printed as 2014-12-29.
Anyone have an idea on why this is?
EDIT: After the comment by Hacketo, here is the rest of the code.
var dates = [];
currentDate = $scope.firstDateOfWeek;
for (i = 0;
(currentDate <= $scope.lastDate ||
(currentDate > $scope.lastDate && currentDate.getDay() != 1)); i++) {
if (currentDate.getMonth() == $scope.month) {
dates.push(new Date(currentDate));
} else {
dates.push(null);
}
currentDate.setDate(currentDate.getDate() + 1);
}
What is happening is that currentDate holds a reference to $scope.firstDateOfWeek, which is getting updated in the for loop.
The only question-mark that remains is then asynchronous behaviour that is showed by console.log(). Could someone explain that?

Javascript - Remove values from array - Constructing an array of times

I am having an issue figuring this out. The idea is that I have an array of values that I'm considering times, and I need to remove all values that overlap with a specific time:
var availableTimes = ['0 - 0.5', '0.5 - 1', '1 - 1.5', '1.5 -2', '2 - 2.5', '2.5 -3']
var timeToRemove = '0.5 - 2'
I have full control over what the array looks like, but that's the idea. I can't figure out how to construct my array of available times and my timeToRemove so that I can accomplish this. I am using a 24:00 hour clock, so 1 - 1.5 is 1:00 AM to 1:30 AM.
Any thoughts on the best way to construct this?
I would prefer a OO way to do it:
(1) Construct an array with objects with following structure:
{
StartTime:[fload],
EndTime:[fload]
}
So your time array looks like this
var availableTimes=
[
{StartTime:0,EndTime:0.5},
{...}
];
(2) The time to be removed has the same structure:
var timeToRemove = '0.5 - 2';
=>
var timeToRemove={StartTime:0.5,EndTime:2};
(3) Delete algorithm looks like this:
for (var i=0;i<availableTimes.length;i++)
{
if(availableTimes[i].StartTime > timeToRemove.StartTime
&& availableTimes[i].EndTime < timeToRemove.EndTime)
availableTimes[i]=null; //perform deletion
}
var availableTimes = ['0 - 0.5', '0.5 - 1', '1 - 1.5', '1.5 -2', '2 - 2.5', '2.5 -3']
var timeToRemove = '0.5 - 2';
var tempar = timeToRemove.replace(/\s/g,"").split('-');
for(var i = 0, t = availableTimes.length; i < t; i++){
var itemar = availableTimes[i].replace(/\s/g,"").split('-');
if(tempar[0] === itemar[0] && tempar[1] === itemar[1]){
availableTimes .splice(i, 1);
return;
}
}
Since you have control over your data structures, I think you're better off structuring it as arrays of length 2 than strings (though you can convert what you have to this form by using split(" - "))
var availableTimes = [[0,0.5], [0.5,1], [1,1.5], [1.5 -2], [2,2.5], [2.5 -3]]
var timeToRemove = [0.5,2]
for (var i = 0; i<availableTimes.length;i++){
//use >= and <= if you want a closed interval
if ((availableTimes[i][0] > timeToRemove[0] &&
availableTimes[i][0] < timeToRemove[1]) ||
(availableTimes[i][1] > timeToRemove[0] &&
availableTimes[i][1] < timeToRemove[1]))
{
console.log("overlapping time: " + availableTimes[i]);
}
}
produces:
overlapping time: 0.5,1
overlapping time: 1,1.5

My first script works! - except for IE and Safari - please help

Hey all,
I'm trying to teach myself Javascript. The following is my first script so bear with me if there are some "rookie" mistakes. :)
It works in FF and Chrome, but not in IE or Safari.
JSLint won't process past the for loop.
IE debugger is giving me an Invalid argument error on the line(#37):
document.getElementById(progBarId).style.width = barWidth + 'px';
I've confirmed that the barWidth variable is a number.
I've Googled IE issues with my syntax and came up empty.
Any help is greatly appreciated. Thanks.
window.onload = upDtProgBars;
function upDtProgBars() {
var allTags = document.getElementsByTagName("div");
for (var i = 0; i < allTags.length; i++) {
if (allTags[i].className.indexOf("progBar") > -1) {
progBarId = setProgBarWidth(allTags[i].id);
}
// END if
}
// END for loop
function setProgBarWidth(progBarId) {
var today = new Date();
var startDate;
var numWeeks;
var barWidth = 0;
var progBarID = "";
switch (progBarId) {
case "html":
startDate = new Date('5,1,2009')
break;
case "html5":
startDate = new Date()
break;
case "js":
startDate = new Date('1, 1, 2011')
break;
case "csharp":
startDate = new Date('9,3, 2010')
break;
default:
}
// END Switch
if (progBarId != "") {
numWeeks = getNumWeeks(today, startDate);
barWidth = parseInt(numWeeks * 2.76);
document.getElementById(progBarId).style.width = barWidth + 'px';
}
// END if not empty string
}
// END setProgBarWidth
function getNumWeeks(d1, d2) {
var ONE_WEEK = 1000 * 60 * 60 * 24 * 7;
var diffInWeeks = Math.round(Math.abs(d2.getTime() - d1.getTime())) / ONE_WEEK;
return diffInWeeks;
}
// END getNumWeeks
}
// END upDtProgBars
Your dates are not formatted properly for IE and Safari. This indirectly causes an invalid argument on the line where you set width because of the following example process:
var date = new Date("garbage"); // Invalid date
var time = date.getTime(); // returns NaN from an invalid date
var barWidth = time * 2.76; // Still NaN
// The following line results in "NaNpx" for the width, which throws an error
document.getElementById(progBarId).style.width = barWidth + 'px';
Stick to using multiple arguments; new Date(year, month, date). Example:
case "csharp":
startDate = new Date(2010, 8, 3);
Remember that months start with 0 in this case, so 8 is September, not August. Also, don't forget your semi-colons at the end of lines.
Date - MDC
I'm not a JavaScript expert and am not sure what is causing the error. But you should pass the object itself rather than just the ID to setProgBarWidth(). Why make your code lookup the same item more than once? That would also rule out any problems related to the ID.
Try to use jquery for DOM manipulation, is much easier and prevents some mistakes.

Instantiating a JavaScript object by calling prototype.constructor.apply

Let me start with a specific example of what I'm trying to do.
I have an array of year, month, day, hour, minute, second and millisecond components in the form [ 2008, 10, 8, 00, 16, 34, 254 ]. I'd like to instantiate a Date object using the following standard constructor:
new Date(year, month, date [, hour, minute, second, millisecond ])
How can I pass my array to this constructor to get a new Date instance? [ Update: My question actually extends beyond this specific example. I'd like a general solution for built-in JavaScript classes like Date, Array, RegExp, etc. whose constructors are beyond my reach. ]
I'm trying to do something like the following:
var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = Date.prototype.constructor.apply(this, comps);
I probably need a "new" in there somewhere. The above just returns the current time as if I had called "(new Date()).toString()". I also acknowledge that I may be completely in the wrong direction with the above :)
Note: No eval() and no accessing the array items one by one, please. I'm pretty sure I should be able to use the array as is.
Update: Further Experiments
Since no one has been able to come up with a working answer yet, I've done more playing around. Here's a new discovery.
I can do this with my own class:
function Foo(a, b) {
this.a = a;
this.b = b;
this.toString = function () {
return this.a + this.b;
};
}
var foo = new Foo(1, 2);
Foo.prototype.constructor.apply(foo, [4, 8]);
document.write(foo); // Returns 12 -- yay!
But it doesn't work with the intrinsic Date class:
var d = new Date();
Date.prototype.constructor.call(d, 1000);
document.write(d); // Still returns current time :(
Neither does it work with Number:
var n = new Number(42);
Number.prototype.constructor.call(n, 666);
document.write(n); // Returns 42
Maybe this just isn't possible with intrinsic objects? I'm testing with Firefox BTW.
I've done more investigation of my own and came up with the conclusion that this is an impossible feat, due to how the Date class is implemented.
I've inspected the SpiderMonkey source code to see how Date was implemented. I think it all boils down to the following few lines:
static JSBool
Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble *date;
JSString *str;
jsdouble d;
/* Date called as function. */
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
int64 us, ms, us2ms;
jsdouble msec_time;
/* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
* so compute ms from PRMJ_Now.
*/
us = PRMJ_Now();
JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
JSLL_DIV(ms, us, us2ms);
JSLL_L2D(msec_time, ms);
return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
}
/* Date called as constructor. */
// ... (from here on it checks the arg count to decide how to create the date)
When Date is used as a function (either as Date() or Date.prototype.constructor(), which are exactly the same thing), it defaults to returning the current time as a string in the locale format. This is regardless of any arguments that are passed in:
alert(Date()); // Returns "Thu Oct 09 2008 23:15:54 ..."
alert(typeof Date()); // Returns "string"
alert(Date(42)); // Same thing, "Thu Oct 09 2008 23:15:54 ..."
alert(Date(2008, 10, 10)); // Ditto
alert(Date(null)); // Just doesn't care
I don't think there's anything that can be done at the JS level to circumvent this. And this is probably the end of my pursuit in this topic.
I've also noticed something interesting:
/* Set the value of the Date.prototype date to NaN */
proto_date = date_constructor(cx, proto);
if (!proto_date)
return NULL;
*proto_date = *cx->runtime->jsNaN;
Date.prototype is a Date instance with the internal value of NaN and therefore,
alert(Date.prototype); // Always returns "Invalid Date"
// on Firefox, Opera, Safari, Chrome
// but not Internet Explorer
IE doesn't disappoint us. It does things a bit differently and probably sets the internal value to -1 so that Date.prototype always returns a date slightly before epoch.
Update
I've finally dug into ECMA-262 itself and it turns out, what I'm trying to achieve (with the Date object) is -- by definition -- not possible:
15.9.2 The Date Constructor Called as a Function
When Date is called as a
function rather than as a constructor,
it returns a string representing the
current time (UTC).
NOTE The function
call Date(…) is not equivalent to the
object creation expression new Date(…)
with the same arguments.
15.9.2.1 Date ( [ year [, month [, date [, hours [, minutes [, seconds [,
ms ] ] ] ] ] ] ] )
All of the
arguments are optional; any arguments
supplied are accepted but are
completely ignored. A string is
created and returned as if by the
expression (new Date()).toString().
I'd hardly call this elegant, but in my testing (FF3, Saf4, IE8) it works:
var arr = [ 2009, 6, 22, 10, 30, 9 ];
Instead of this:
var d = new Date( arr[0], arr[1], arr[2], arr[3], arr[4], arr[5] );
Try this:
var d = new Date( Date.UTC.apply( window, arr ) + ( (new Date()).getTimezoneOffset() * 60000 ) );
This is how you might solve the specific case:-
function writeLn(s)
{
//your code to write a line to stdout
WScript.Echo(s)
}
var a = [ 2008, 10, 8, 00, 16, 34, 254 ]
var d = NewDate.apply(null, a)
function NewDate(year, month, date, hour, minute, second, millisecond)
{
return new Date(year, month, date, hour, minute, second, millisecond);
}
writeLn(d)
However you are looking for a more general solution. The recommended code for creating a constructor method is to have it return this.
Hence:-
function Target(x , y) { this.x = x, this.y = y; return this; }
could be constructed :-
var x = Target.apply({}, [1, 2]);
However not all implementations work this way not least because the prototype chain would be wrong:-
var n = {};
Target.prototype = n;
var x = Target.apply({}, [1, 2]);
var b = n.isPrototypeOf(x); // returns false
var y = new Target(3, 4);
b = n.isPrototypeOf(y); // returns true
It's less than elegant, but here's a solution:
function GeneratedConstructor (methodName, argumentCount) {
var params = []
for (var i = 0; i < argumentCount; i++) {
params.push("arguments[" + i + "]")
}
var code = "return new " + methodName + "(" + params.join(",") + ")"
var ctor = new Function(code)
this.createObject = function (params) {
return ctor.apply(this, params)
}
}
The way this works should be pretty obvious. It creates a function through code generation. This example has a fixed number of parameters for each constructor you create, but that's useful anyway. Most of the time you have atleast a maximum number of arguments in mind. This also is better than some of the other examples here because it allows you to generate the code once and then re-use it. The code that's generated takes advantage of the variable-argument feature of javascript, this way you can avoid having to name each parameter (or spell them out in a list and pass the arguments in to the function you generate). Here's a working example:
var dateConstructor = new GeneratedConstructor("Date", 3)
dateConstructor.createObject( [ 1982, 03, 23 ] )
This will return the following:
Fri Apr 23 1982 00:00:00 GMT-0800 (PST)
It is indeed still...a bit ugly. But it atleast conveniently hides the mess and doesn't assume that compiled code itself can get garbage collected (since that may depend on the implementation and is a likely area for bugs).
Cheers,
Scott S. McCoy
This is how you do it:
function applyToConstructor(constructor, argArray) {
var args = [null].concat(argArray);
var factoryFunction = constructor.bind.apply(constructor, args);
return new factoryFunction();
}
var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);
It will work with any constructor, not just built-ins or constructors that can double as functions (like Date).
However it does require the Ecmascript 5 .bind function. Shims will probably not work correctly.
By the way, one of the other answers suggests returning this out of a constructor. That can make it very difficult to extend the object using classical inheritance, so I would consider it an antipattern.
With ES6 syntax, there's at least 2 methods to achieve this:
Spread Operator
Reflect.construct
var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
// with the spread operator
var d1 = new Date(...comps);
// with Reflect.construct
var d2 = Reflect.construct(Date, comps);
console.log('d1:', d1, '\nd2:', d2);
// or more readable:
console.log(`d1: ${d1}\nd2: ${d2}`);
It will work with ES6 spread operator.
You simply:
const arr = [2018, 6, 15, 12, 30, 30, 500];
const date = new Date(...arr);
console.log(date);
You can do it with flagrant, flagrant abuse of eval:
var newwrapper = function (constr, args) {
var argHolder = {"c": constr};
for (var i=0; i < args.length; i++) {
argHolder["$" + i] = args[i];
}
var newStr = "new (argHolder['c'])(";
for (var i=0; i < args.length; i++) {
newStr += "argHolder['$" + i + "']";
if (i != args.length - 1) newStr += ", ";
}
newStr += ");";
return eval(newStr);
}
sample usage:
function Point(x,y) {
this.x = x;
this.y = y;
}
var p = __new(Point, [10, 20]);
alert(p.x); //10
alert(p instanceof Point); //true
enjoy =).
function gettime()
{
var q = new Date;
arguments.length && q.setTime( ( arguments.length === 1
? typeof arguments[0] === 'number' ? arguments[0] : Date.parse( arguments[0] )
: Date.UTC.apply( null, arguments ) ) + q.getTimezoneOffset() * 60000 );
return q;
};
gettime(2003,8,16)
gettime.apply(null,[2003,8,16])
I know it's been a long time, but I have the real answer to this question. This is far from impossible. See https://gist.github.com/747650 for a generic solution.
var F = function(){};
F.prototype = Date.prototype;
var d = new F();
Date.apply(d, comps);
Here is another solution:
function createInstance(Constructor, args){
var TempConstructor = function(){};
TempConstructor.prototype = Constructor.prototype;
var instance = new TempConstructor;
var ret = Constructor.apply(instance, args);
return ret instanceof Object ? ret : instance;
}
console.log( createInstance(Date, [2008, 10, 8, 00, 16, 34, 254]) )
Edited
Sorry, I was sure I made it that way years ago, right now I'll stick to:
var d = new Date(comps[0],comps[1],comps[2],comps[3],comps[4],comps[5],comps[6]);
Edit:
But do remember that a javascript Date-object uses indexes for months, so the above array means
November 8 2008 00:16:34:254
var comps = [ 2008, 10, 8, 00, 16, 34, 254 ];
var d = eval("new Date(" + comps.join(",") + ");");

Categories

Resources