I am trying to implement a custom sorting in slick grid. I am using this as a reference https://github.com/mleibman/SlickGrid/wiki/DataView#sorting. I want to force rows with specific properties to always sort to the bottom of the grid. (I tried to get a working example of slickgrid to work in jsfiddle.net but I couldn't.) I have tried this but it's not working.
var gridData =[
{ Id:1, Code: '232046', Depth2: 4000,},
{ Id:2, Code: '23247', Depth2: 2000 },
{ Id:3, Code: '12543', Depth2: 1500, rowoption_stickyorder: 1 }
];
grid.onSort.subscribe(function (e, args) {
var footerOrderProperty = 'rowoption_stickyorder';
var comparer = function (a, b) {
var result = (a[args.sortCol.field] > b[args.sortCol.field]) ? 1 : -1;
if (b[footerOrderProperty] != undefined || a[footerOrderProperty] != undefined)
result = -1;
return result;
}
dataview.sort(comparer, args.sortAsc);
});
What am I missing?
if (b[footerOrderProperty] != undefined || a[footerOrderProperty] != undefined)
result = -1;
Won't work. false || true places an object/row with the property above one that does not have it.
var bb = b[footerOrderProperty],
aa = a[footerOrderProperty];
if(aa && !bb){
result = 1;
}
else if(!aa && bb)
{
result = -1
}
See my fiddle
Note: Fiddle won't work in Chrome due to the mime type issue and the common workaround of making the resource rawgithub doesn't seem to be working.
Origineil, great work! Your suggestion got me 99% of the way there. I really want to have the columns sortable in both directions and keep the rows at the bottom. I modified your code to take in account the sortOrder and it works like a champ.
grid.onSort.subscribe(function (e, args) {
console.log(args.sortAsc)
var footerOrderProperty = 'rowoption_stickyorder';
var comparer = function (a, b) {
var result = (a[args.sortCol.field] > b[args.sortCol.field]) ? 1 : -1;
var bb = b[footerOrderProperty],
aa = a[footerOrderProperty];
if (aa && !bb) {
result = args.sortAsc ? 1 : -1;
} else if (!aa && bb) {
result = args.sortAsc ? -1 : 1;
} else if (aa != undefined && bb != undefined) {
result = (aa > bb) ? 1 : -1;
result *= args.sortAsc ? 1 : -1
}
console.log(a.id + " | " + b.id + " " + result)
return result
}
dataView.sort(comparer, args.sortAsc);
});
Here is the updated fiddle.
Related
The code below shows my current workings. the commented out line sorts my divs but leaves the empty divs at the top.
the 4 lines above do not make a difference
I think I'm missing something really simple here
//Group 1
var alphabeticallyOrderedDivs7 = $('.group1').sort(function getSort(a, b) {
if($(a).find(a).text() === "") return 1;
if($(b).find(b).text() === "") return -1;
if($(a).find(a).text() === $(b).find(b).text()) return 0;
return $(a).find(a).text() < $(b).find(b).text()) ? -1 : 1;
//return String.prototype.localeCompare.call($(a).text().toLowerCase(), $(b).text().toLowerCase());
});
You can simply do this:
//Group 1
var alphabeticallyOrderedDivs7 = $('.group1').sort(function getSort(a, b) {
//Assuming $(a).text() and $(b).text() is the content of the div
return $(a).text() < $(b).text() ? 1 : $(a).text() > $(b).text() ? -1 : 0;
});
I have this function:
tests.sort(function (a, b) {
var diff = a.title.localeCompare(b.title);
return diff == 0 ? b.modifiedDate.localeCompare(a.modifiedDate) : diff;
});
I am using it to sort the tests array first by title and then by modifiedDate. The code was working but now I found it gives an error.
When the modifiedDate is null and when this happens the compare fails.
How could I make it so that if the modifiedDate is null then the sort still works and places those rows after the rows with a modifiedDate that's not null?
Thinking off the top of my head, to sort by title and then modifiedDate with nulls last:
tests.sort(function (a, b) {
var diff = a.title.localeCompare(b.title),
has_modifiedDate = a.modifiedDate && b.modifiedDate && true || false;
if (diff === 0) {
if (has_modifiedDate) {
return a.modifiedDate.localeCompare(b.modifiedDate)
}
else {
if (! b.modifiedDate){
if (! a.modifiedDate)
return 0;
else
return -1;
}
else {
if (! a.modifiedDate)
return 1;
else
return -1;
}
}
}
else
return diff;
});
Note: this is untested and it's very late/early here. If this is incorrect, post and I'll delete or update; but way to tired to think.
Quick dataset you can try testing with; fill in with whatever data you want:
var tests = [
{modifiedDate:'z', title:'foo'},
{modifiedDate:'a', title:'foo'},
{modifiedDate:null, title:'foo'},
{modifiedDate:'a', title:'foo'},
{modifiedDate:'null', title:'foo'},
{modifiedDate:'z', title:'foo'},
{modifiedDate:'z', title:'bar'},
{modifiedDate:'a', title:'bar'},
{modifiedDate:null, title:'bar'}
];
Try this:
tests.sort(function(a, b) {
var diff = a.title.localeCompare(b.title);
return diff == 0 ? (a.modifiedDate ? a.modifiedDate.getTime() : Infinity) - (b.modifiedDate ? b.modifiedDate.getTime() : Infinity) : diff;
})
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!
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;
}
};
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.
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;
}
}
I am trying to organize a observablearray that has inside 2 boolean values and a price. I need via knockout and 2 checkboxes, filter the elements by these two values. Also sort by price ( ascending and descending) the displayed values . I don't put any code because I'm new in knockout and I can't see the way to make these actions.
Appreciate someone who instructed me.
Simple answer, I tried with this, but making some changes on my personal viewModel to supply my needs. So, I make something like this:
self.elementsToShow = ko.pureComputed(function () {
// Represents a filtered and ordered list of elements
var recomend = self.showRecommended(); //chekbox 1
var special = self.showSpecial(); // checkbox2
var sorting = self.currentSortDirection(); //sort direction: price or rating //ascending or descending, represented by an observableArray with that conditions and the //selectedSortDirection
if (!recomend && !special) return self.myOservableArray().sort(function (a, b) {
//in case that no one of the checkboxes where selected but the sort direction was't by default
if (sorting.price != null) {
var fp = sorting.price ? -1 : 1;
ap = parseInt(a.price);
bp = parseInt(b.price);
return ap == bp ? 0 : (fp * (ap < bp ? -1 : 1));
}
else if (sorting.rate != null) {
var f = sorting.rate ? -1 : 1;
ar = parseFloat(a.rating);
br = parseFloat(b.rating);
return ar == br ? 0 : (f * (ar < br ? -1 : 1));
}
});
return ko.utils.arrayFilter(self.myOservableArray(), function (element) {
return (element.recommended != "0" && recomend) || (element.offer != "" && special); //some other conditions for the relection of the checkboxes in the observableArray
}).sort(function (a, b) {
if (sorting.price != null) {
var fs = sorting.price ? -1 : 1;
ap = a.price;
bp = b.price;
return ap == bp ? 0 : (fs * (ap < bp ? -1 : 1));
}
if (sorting.rate != null) {
var fu = sorting.rate ? -1 : 1;
ar = a.rating;
br = b.rating;
return ar == br ? 0 : (fu * (ar < br ? -1 : 1));
}
});
}, self);
The following is a natural sort function I pulled from somewhere I forget exactly. I'm looking to modify it so that empty or null values always sort to the bottom regardless of asc/desc.
Here is what I have right now:
function gridNaturalSorter(a, b) {
if(a[sortcol])
a = a[sortcol].replace(/<(?:.|\n)*?>/gm, '');
if(b[sortcol])
b = b[sortcol].replace(/<(?:.|\n)*?>/gm, '');
if(b)
b = b.toString().substr(0, 15);
if(a)
a = a.toString().substr(0, 15);
var re = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi,
sre = /(^[ ]*|[ ]*$)/g,
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
hre = /^0x[0-9a-f]+$/i,
ore = /^0/,
i = function(s) {
return gridNaturalSorter.insensitive && (''+s).toLowerCase() || ''+s
},
// convert all to strings strip whitespace
x = i(a).replace(sre, '') || '',
y = i(b).replace(sre, '') || '',
// chunk/tokenize
xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
// numeric, hex or date detection
xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
oFxNcL, oFyNcL;
// first try and sort Hex codes or Dates
if (yD)
if ( xD < yD ) return -1;
else if ( xD > yD ) return 1;
// natural sorting through split numeric strings and default strings
for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
// handle numeric vs string comparison - number < string - (Kyle Adams)
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
return (isNaN(oFxNcL)) ? 1 : -1;
}
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
else if (typeof oFxNcL !== typeof oFyNcL) {
oFxNcL += '';
oFyNcL += '';
}
if (oFxNcL < oFyNcL)
return -1;
if (oFxNcL > oFyNcL)
return 1;
}
return 0;
}
If you know how to implement multiple comparators and a comparator that sorts null to bottom that's quite easy.
To implement multiple comparators, you just have to return the result of the first comparator that doesn't return 0.
Here I also created a withComparators helper function that allows to compose multiple comparators together. If you understand this code you will be able to easily come up with your own solution for your specific problem.
Note that your gridNaturalSorter function is a comparator just like nullsToBottom is in my example.
E.g.
var items = ['test', null, 'test1', 'test3', null, 'test4'];
items.sort(withComparators(nullsToBottom, textAsc));
//["test", "test1", "test3", "test4", null, null]
function nullsToBottom(a, b) {
return a === b? 0 : a === null? 1 : -1;
}
function textAsc(a, b) {
return a < b? -1 : +(a > b);
}
function withComparators() {
var comparators = arguments;
return function (a, b) {
var len = comparators.length, i = 0, result;
for (; i < len; i++) {
result = comparators[i](a, b);
if (result) return result;
}
return 0;
};
}