-1

I have a function which orders dates based on a pivot. Note that this function ignores the year of the supplied dates. For example, given a pivot date of 1-July, all dates will be ordered from 1-July to 31-June. The problem seems to be with my sort compare function which works perfectly fine in Chrome and Firefox, however fails in IE. The resulting statsDataPoints (Array) does not get sorted.

This is what the dates array looks like:

[{"Min":"6.0", "Mean":"10.8", "Max":"32.1", "StdDev":"7.5", "LowerPercentile":"6.7", "Median":"8.0", "UpperPercentile":"11.2", "_Name":"30-Mar"}, {"Min":"6.0", "Mean":"11.1", "Max":"31.7", "StdDev":"7.4", "LowerPercentile":"7.3", "Median":"8.7", "UpperPercentile":"11.1", "_Name":"31-Mar"}, {"Min":"6.0", "Mean":"10.9", "Max":"31.4", "StdDev":"7.3", "LowerPercentile":"7.2", "Median":"8.4", "UpperPercentile":"11.0", "_Name":" 1-Apr"}, ... ]

This is my function:

/**
 * Returns a function which orders dates based on a pivot. Note that this function ignores the year of the
 * supplied dates. It is easiest to illustrate how this function works with an example:
 * Given a pivot date of 1-July, all dates will be ordered from 1-July to 31-June. 
 *
 * @param {Array}  dates    An array of objects containing a date key.
 * @param {String} pivot    The pivot date we are sorting from (should be in the same format as dateKey)
 */

var dummyYear = 2000;
var pivot = moment(readingStartDate).format("DD-MMM");
pivot = moment(pivot, "DD-MMM").year(dummyYear);

statsDataPoints.sort(function(aDate, bDate) {

 var a = moment(aDate["_Name"], "DD-MMM").year(dummyYear).toDate();
 var b = moment(bDate["_Name"], "DD-MMM").year(dummyYear).toDate();

 if (((a < pivot) && (pivot < b)) || (a > b)) {
  return 1;
 } else if (((b < pivot) && (pivot < a)) || (a < b)) {
  return -1;
 }
 return 0;

});
  • So, have you debugged this to see at what point which variables might have significantly other values in IE than in the other browsers ...? – CBroe Mar 29 '17 at 20:59
  • moment has a function to compare dates. isBefore | isAfter. Check the docs – yBrodsky Mar 29 '17 at 21:01
  • Sorry, I don't get what you want to do or what "*all dates will be ordered from 1-July to 31-June*" means. What happens to dates that are not in June? What is special about the pivot? I suspect the`sort` function doesn't get it either. – Bergi Mar 29 '17 at 21:21
  • Thanks @yBrodsky yes I had used those previously, I've updated script now to: `if (a.isBefore(pivot) && pivot.isBefore(b)) return 1; if (b.isBefore(pivot) && pivot.isBefore(a)) return -1; if (a.isAfter(b)) return 1; if (a.isBefore(b)) return -1; return 0;` – Zaid Al-Dabbagh Mar 29 '17 at 21:49
  • The issue here is not the browser, but your sort algorithm which is sensitive to the order of the source data. – RobG Mar 31 '17 at 00:01

3 Answers3

0

Your comparison function is inconsistent: Assuming x=1, y=3 and pivot=2 you get compare(x, y) == 1 but also compare(y, x) == 1 because of the || in the first condition.

I suspect you meant to use

if ((a < pivot) && (pivot < b))
    return 1;
if ((b < pivot) && (pivot < a))
    return -1;
if (a > b) {
    return 1;
if (a < b)
    return -1;
return 0;
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

Thanks @CBroe, @yBrodsky and @Bergi for your replies. I managed to resolve this by simplifying my code, and not using a sort, which is unfortunate but IE just wasn't handling the sort compare function really well.

Given that my data series was in the correct order, I just simply shifted my data points to the end of the array to ensure that my series start on current date.

To clarify this, this is how I do this:

// shift series by number of days since start of the year
var startOfYeat = moment().startOf('year');
var curDate = moment();
var daysSinceStartOfYear = curDate.diff(startOfYeat, 'days');

for (i = 0; i < daysSinceStartOfYear; i++) {
    statsDataPoints.push(statsDataPoints.shift());
}

It is a shame that IE doesn't seem to support a simple compare function, so if anyone has other ideas as to why the sort fails in IE do leave a comment here.

  • "*It is a shame that IE doesn't seem to support a simple compare function*" but you haven't shown that IE's logic is flawed. The compare function compares Date objects to Moment objects, which may be the cause of your issue. – RobG Mar 30 '17 at 05:43
0

The issue is that your sort algorithm is sensitive to the order in which dates are passed to it. IE uses a different sort algorithm so gets the source data in a different order to Firefox, so sometimes you get a different result and sometimes you don't, depending on the order of dates in the original array.

So not IE's fault, you should be grateful that it helpd identify an issue with your algorithm. ;-)

There some other flaws in your code. If you follow the error messages provided by moment.js, you'll see that the line:

var pivot = moment(readingStartDate).format("DD-MMM");

Throws a warning:

Deprecation warning: value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info. Arguments: [0] _isAMomentObject: true, _isUTC: false, _useUTC: false, _l: undefined, _i: 31-Mar, _f: undefined, _strict: undefined, _locale: [object Object] undefined

Changing that line to:

var pivot = moment(readingStartDate,'DD-MM').format('DD-MMM');

removes the warning. Also, in the line:

(a < pivot) && (pivot < b)

you are comparing a moment object (pivot), to Date objects (a and b). The simplest fix is if pivot is also a date, so:

var pivot = moment(readingStartDate,'DD-MM').format('DD-MMM').toDate();

You can run the following in IE 7, but unfortunately SO snippets don't support even IE 10 so you'll have to run it elsewhere to test IE. It runs in Firefox 38 though.

It demonstrates that passing the same values in a different order produces a different result.

var statsDataPoints1 = [{"_Name": "30-Mar"}, {"_Name": " 1-Apr"}, {"_Name": "31-Mar"}];
var statsDataPoints2 = [{"_Name": "30-Mar"}, {"_Name": "31-Mar"}, {"_Name": " 1-Apr"}];

var readingStartDate = "31-Mar";
var dummyYear = 2000;
var pivot = moment(readingStartDate, 'DD-MM').format("DD-MMM");
pivot = moment(pivot, "DD-MMM").year(dummyYear).toDate();

function doStuff(statsDataPoints) {
  statsDataPoints.sort(function(aDate, bDate) {

    var a = moment(aDate["_Name"], "DD-MMM").year(dummyYear).toDate();
    var b = moment(bDate["_Name"], "DD-MMM").year(dummyYear).toDate();

    if (((a < pivot) && (pivot < b)) || (a > b)) {
      return 1;
    } else if (((b < pivot) && (pivot < a)) || (a < b)) {
      return -1;
    }
    return 0;
  });
}
   
// This source data sequence gives different results in Firefox and IE
doStuff(statsDataPoints1); 
for (var i = 0, iLen = statsDataPoints1.length; i < iLen; i++) {
  console.log(statsDataPoints1[i]._Name);
};

// Same data values as statsDataPoints1, different sequence
// Result has the same order in Firefox and IE
doStuff(statsDataPoints2); 
for (var i = 0, iLen = statsDataPoints2.length; i < iLen; i++) {
  console.log(statsDataPoints2[i]._Name);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>
RobG
  • 142,382
  • 31
  • 172
  • 209