简体   繁体   中英

Javascript custom sort function for dates with blanks at the bottom

I am using a table plugin (ng-grid) to display a bunch of data which includes some date columns. The plugin allows custom sorting with a "sortingAlgorithm" function, which accepts "aDate" and "bDate" parameters. The dates are stored as strings in the database, and I'm using Moment to convert them to date objects. Here is the sort function:

sortingAlgorithm: function (aDate, bDate) {
    var a = moment(aDate, 'MM/DD/YYYY');
    var b = moment(bDate, 'MM/DD/YYYY');
    if (a.isBefore(b)) {
        return -1;
    }
    else if (a.isAfter(b)) {
        return 1;
    }
    else {
        return 0;
    }
}

This works fine, but if there is no date, just an empty string, the blanks are going at the end of the list when sorted in ascending order, but at the beginning when sorted descending. What can I do to ensure that blanks ("") are always moved to the bottom of the list?

Thanks.

UPDATE

I think this has something to do specifically with the UI-Grid library. See rowSorter.js . It seems to handle nulls internally, which is gumming up my mojo. I also don't see where the selected sort direction is exposed for me to work with...

I'm adding the "angular-ui-grid" tag...

So I took a copy of ui-grid's internal date-specific sorting function, and replaced their "handleNulls" function call with my own (see below):

sortingAlgorithm: function (a, b) {
    var nulls = handleNulls(a, b);
    if ( nulls !== null ){
        return nulls;
    } else {
        if (!(a instanceof Date)) {
            a = new Date(a);
        }
        if (!(b instanceof Date)){
            b = new Date(b);
        }
        var timeA = a.getTime(),
            timeB = b.getTime();

        return timeA === timeB ? 0 : (timeA < timeB ? -1 : 1);
    }
}

And here is a copy of ui-grid's "handleNulls" function, updated to always force nulls to the bottom based on the direction:

function handleNulls(a, b) {
    if ((!a && a !== 0 && a !== false) || (!b && b !== 0 && b !== false)) {
        if ((!a && a !== 0 && a !== false) && (!b && b !== 0 && b !== false)) {
            return 0;
        }
        else if (!a && a !== 0 && a !== false && vm.direction === 'asc') {
            return 1;
        }
        else if (!b && b !== 0 && b !== false && vm.direction === 'asc') {
            return -1;
        }
        else if (!a && a !== 0 && a !== false && vm.direction === 'desc') {
            return -1;
        }
        else if (!b && b !== 0 && b !== false && vm.direction === 'desc') {
            return 1;
        }
    }
    return null;
};

vm.direction comes from ui-grid's onRegisterApi callback, which has a hook for sort changes:

onRegisterApi: function (gridApi) {
    vm.gridApi = gridApi;
    vm.gridApi.core.on.sortChanged($scope, function(grid, sortColumns) {
        if (sortColumns[0]) {
            vm.direction = sortColumns[0].sort.direction;
        } else {
            vm.direction = 'none';
        }
    });
}

Works like a charm!

If the ng-grid plugin uses the sorting algorithm and then applies a reverse() to sort in descending order, then I don't think you can force the items at the end. You would have to overwrite the sorting in the plugin I guess.

In order to test for empty string, you could put your comparison condition first and so '' is always at the end. This way you are not creating an invalid date by forcing moment.js to parse an empty string.

sortingAlgorithm: function(aDate, bDate) {
  if (aDate === '') {
    return 1;
  }
  if (bDate === '') {
    return -1;
  }

  var a = moment(aDate, 'MM/DD/YYYY');
  var b = moment(bDate, 'MM/DD/YYYY');

  if (a.isBefore(b)) {
    return -1;
  } else if (a.isAfter(b)) {
    return 1;
  } else {
    return 0;
  }
}

Try this:

var direction=1;
var sortingAlgorithm= function (aDate, bDate) {
    var a=moment(aDate,'MM/DD/YYYY');
    var b=moment(bDate,'MM/DD/YYYY');
    if (!a.isValid()) return 1;
    if (!b.isValid()) return -1;
    return direction*((+a)-(+b));
}

It takes into account the validity of the date and direction (1 or -1), so that the invalid dates are always at the bottom.

Here is what I came up with based on this and another thread using moment.js

 var sortmedate = function (a, b, rowA, rowB, direction) {
            //the dates do not get sorted properly so we need moment.js and this method.
            var dateFormat = 'MM/DD/YYYY'; //todo: pass in date format from mvc controller.

            if (!a && !b) {
                return 0;
            }

            if (!a) {
                return 1;
            }

            if (!b) {
                return -1;
            }

            var firstDate = moment(a, dateFormat);
            if (!firstDate.isValid()) {
                return -1;
            }

            var secondDate = moment(b, dateFormat);
            if (!secondDate.isValid()) {
                return 1;
            }

            if (firstDate.isSame(secondDate)) {
                return 0;
            } else {
                return firstDate.isBefore(secondDate) ? -1 : 1;
            }

        };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM