按日期排序数组会产生意想不到的结果

Sort array by date gives unexpected results

这听起来是个简单的问题,但我整个星期天都在试图弄清楚下面描述的我的实现有什么问题,所以我将它发布到 SO 作为最后的手段。

我有一个 javascript 应用程序可以从服务器接收数据结构。服务器端出于性能原因发送未排序的数据。

这里是 javascript 代码接收数据的片段:

    var seriesRawDataArray = ko.observableArray();
    ...
    analyticscontext.series(seriesRawDataArray).done(function () {
        renderSeries();
    });

analyticscontext模块查询数据使用ajax:

function series(seriesData) {
    return $.ajax({
        url: "/api/analytics/series",
        type: "GET",
        success: function (data) {
            return seriesData(data);
        }
    });
}

renderSeries 在呈现数据之前对数据执行排序:

    // Sort the data by date using moment.js
    seriesRawDataArray.sort(function (left, right) {
        var leftDate = moment.utc(left.timeStamp);
        var rightDate = moment.utc(right.timeStamp);
        var diff = leftDate.diff(rightDate);
        return diff > 0;
    });

问题

这是我从服务器收到的数据样本:

注意末尾未分类的项目。 seriesRawDataArray.sort 似乎对原始数组没有影响,无论我在排序方法中更改什么,原始数组都不会排序。输出总是:

注意这里的未排序元素。 我正在使用的库和数据绝对不是问题,因为这个 jsfiddle 工作得很好! 这段代码有问题吗?

简答

您应该 return 两个日期之间的差异,而不是布尔值:

// sort the data by date using moment.js
seriesRawDataArray.sort(function (left, right) {
    return moment.utc(left.timeStamp).diff(moment.utc(right.timeStamp))
});

为什么

Array.prototype.sort 期望 return 为负值、零值或正值。通常,您会像这样编写排序函数:

yourArray.sort(function (a, b) {
    if (a < b) {            // a comes first
        return -1
    } else if (b < a) {     // b comes first
        return 1
    } else {                // equal, so order is irrelevant
        return 0            // note: sort is not necessarily stable in JS
    }
})

传递给排序的匿名函数用作排序函数的本机实现的比较器。

但是,您的负值不必是 -1,您的正值也不必是 +1。因此,在对数字进行排序时,您可以改用快捷方式:

yourArray.sort(function (a, b) {
    return a - b
})

在 JavaScript 中,减去两个日期会将它们都转换为数字,这就是为什么我们可以使用 return moment.utc(left.timeStamp).diff(moment.utc(right.timeStamp))

(而不是直接减法-,此方法使用moment.js库中的moment.prototype.diff

但是,在您的代码中,您return编辑了diff > 0,它可以是truefalse。由于类型强制,JavScript 会将 true 读作 1,将 false 读作 0。这意味着您的排序函数永远不会 return -1。因此,您的元素将无法正确排序。

let sortedDates = dates.sort(function(a, b){
  return moment(b).format('X')-moment(a).format('X')
});

既然可以格式化有效日期,最好的方法就是使用排序方法javascript,所以在将日期格式化为时间戳时,基本上是按数字排序。

参考文献:

http://www.momentjs.com/docs/#/displaying/format

http://www.w3schools.com/jsref/jsref_sort.asp