sorting showing unexpected results - sorting

I am sorting a grid with knockout.js. Following is my sort function
this.sortByName = function() {
var event = arguments[1];
var targeElement = event.originalTarget;
// console.info(targeElement);
console.log(targeElement.attributes[1].nodeValue);
order = 'sorting';
configuration.data.sort(function(a, b) {
if(a.name<b.name){
order = 'sorting_desc';
return a.name > b.name ? -1 : 1;
}
else if(a.name>b.name){
order = 'sorting_asc'
return a.name < b.name ? -1 : 1;
}
});
$(targeElement).removeClass('sorting_asc sorting_desc').addClass(order);
};
The by default grid view
The Sorted image 1
The sorted image 2
As you can see the order of sorting is not correct. I have been playing around for 3 days in this issue.

Well I found a solution to this problem
this.sortByName = function() {
currentOrder = arguments[0].sortClass();
var sortColumn = arguments[0].rowText;
if(currentOrder =='sorting' || currentOrder =='sorting_desc'){
currentOrder='sorting_asc';
configuration.data.sort(function(a, b) {
if (a[sortColumn] == b[sortColumn])
return 0;
else if (a[sortColumn] < b[sortColumn])
return -1;
else
return 1;
});
}else{
currentOrder='sorting_desc';
configuration.data.sort(function(a, b) {
if (a[sortColumn] == b[sortColumn])
return 0;
else if (a[sortColumn] > b[sortColumn])
return -1;
else
return 1;
});
}
self.resetSortColumns();
arguments[0].sortClass(currentOrder);
};

Related

Why is my result variable not being changed?

So I am trying to solve N-Queens problem, and when I console.log(result) inside the solve function I am getting the correct output. but when I return the result array in the end, it seems like the result array has not been mutated at all, as I get [[-, -, -,-], [-,-,-,-]]
function solveNQueens(n) {
let result = [];
let buffer = Array(n).fill('-');
function solve(bufferIndexRow) {
if (bufferIndexRow === n) {
result.push(buffer);
return console.log('FOUND ONE SOLUTION'); // Finished last row, filled queens array
}
for (let column = 0; column < n; column++) {
buffer[bufferIndexRow] = column;
if (isValid(bufferIndexRow, column)) {
solve(bufferIndexRow + 1);
}
buffer[bufferIndexRow] = '-'
}
}
function isValid(row, col) {
for(let i = 0; i< row; i++) {
if(buffer[i] == col || Math.abs(i - row) == Math.abs(buffer[i]- col))
return false;
}
return true;
}
solve(0);
return result;
}
console.log(solveNQueens(4));
you need to pass in buffer to solve so that it can push the updated buffer to result

Jquery datatables date sorting

I'm using jQuery DataTables, trying to sort data column with 2 different date types.
I have 2 different formats month/year and day/month/year but they are not sorting correctly.
Current code:
function dateSorter(a, b) {
var datea = a.split("/");
var dateb = b.split("/");
if (datea[1] > dateb[1]) return 1;
if (datea[1] < dateb[1]) return -1;
if (datea[0] > dateb[0]) return 1;
if (datea[0] < dateb[0]) return -1;
if( datea.length == 2 )
{
if (datea[2] > dateb[2]) return 1;
if (datea[2] < dateb[2]) return -1;
}
else
{
if( datea[1] > dateb[2] ) return 1;
if( datea[2] < dateb[1] ) return -1;
}
return 0;
}
Above is current code, it works for month/year format and sorts them fine , but not mixed with both formats.
This currently sorts day/month from day/month/year format but it does not sort year correctly.
Example (current):
03/04/2016
12/04/2017
12/05/2015
01/2015
02/2015
02/2016
01/2018
...
It should be:
01/2015
02/2015
12/05/2015
02/2016
03/04/2016
12/04/2017
01/2018
$.extend( jQuery.fn.dataTableExt.oSort, {
"date-time-odd-pre": function (a){
if(/\d{1,2}\/\d{1,2}\/\d{4}/.test(a)){
return parseInt(moment(a, "DD/MM/YYYY").format("X"), 10);
}else{
return parseInt(moment(a, "MM/YYYY").format("X"), 10);
}
},
"date-time-odd-asc": function (a, b) {
return a - b;
},
"date-time-odd-desc": function (a, b) {
return b - a;
}
});
Should work, though you'll need moment available.
Hope that helps.

Calling sort on slickgrid

In the slickgrid I'm able to set the sort column and it's sort direction using the grid.SetSortColumn(colName,true/false). This only sets the sorting glyph but does no sorting. Is there a way to call the sort event handler. I've defined the sort handler like grid.onSort.subscribe(function(){});
The behavior you are observing is correct.
grid.setSortColumn(columnId, isAsc);
only updates the glyph on the sort column.
In your case, you will initially need to sort the data, and then use setSortColumn to update the glyph on sortColumn. You can reuse sorter used in onSort event like this:
var gridSorter = function(columnField, isAsc, grid, gridData) {
var sign = isAsc ? 1 : -1;
var field = columnField
gridData.sort(function (dataRow1, dataRow2) {
var value1 = dataRow1[field], value2 = dataRow2[field];
var result = (value1 == value2) ? 0 :
((value1 > value2 ? 1 : -1)) * sign;
return result;
});
grid.invalidate();
grid.render();
}
var grid = new Slick.Grid($gridContainer, gridData, gridColumns, gridOptions);
//These 2 lines will sort you data & update glyph while loading grid
//columnField is field of column you want to sort initially, isAsc - true/false
gridSorter(columnField, isAsc, grid, gridData);
//I had the columnField, columnId same else used columnId below
grid.setSortColumn(columnField, isAsc);
grid.onSort.subscribe(function(e, args) {
gridSorter(args.sortCol.field, args.sortAsc, grid, gridData);
});
How I arrived on this solution?
Read comments here. https://github.com/mleibman/SlickGrid/issues/325
dataView.fastSort does the job. You can then use setSortColumn to set the sorting glyph.
I have multiple column sort enabled, I had to change the function to pass the correct sort column.
grid.onSort.subscribe(function(e, args) {
gridSorter(**args.sortCols[0].sortCol.field**, **args.sortCols[0].sortAsc**, grid, gridData);
});
You can trigger click event on the column header...which does sorting
I fixed the issue like this...
$('.slick-header-columns').children().eq(0).trigger('click'); // for first column
I was inspired by Mr.Hunts answer but I took a slightly different approach to extend the current grid.setSortColumn(columnId, isAsc) to grid.setInitialSortColumn(columnId, isAsc). This will apply the sort and do everything grid.setSortColumn does.
var thisGrid = { //Your grid obj
columns: , // Your columns object
grid: , // new Slick.Grid....
}
thisGrid.grid.onSort.subscribe(function (e, args) { // ar var cols = args.sortCols;]
thisGrid.grid.customSort(args);
});
thisGrid.grid.customSort = function (args) {
var cols = args.sortCols;
thisGrid.dataView.sort(function (dataRow1, dataRow2) {
if (cols) {
for (var i = 0, l = cols.length; i < l; i++) {
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field],
value2 = dataRow2[field];
var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result != 0) {
return result;
}
}
}
return 0;
});
}
thisGrid.grid.setInitialSortColumn = function (columnId, ascending) {
thisGrid.grid.setInitialSortColumns([{
columnId: columnId,
sortAsc: ascending
}
]);
};
thisGrid.grid.setInitialSortColumns = function (cols) {
sortColumns = cols;
$.each(sortColumns, function (i, col) {
var columnIndex = thisGrid.grid.getColumnIndex(col.columnId);
var column = thisGrid.columns[columnIndex];
if (col.sortAsc == null) {
col.sortAsc = true;
}
var args = {
grid: thisGrid.grid,
multiColumnSort: true,
sortCols: [{
sortCol: column,
sortAsc: col.sortAsc
}
]
}
thisGrid.grid.setSortColumn(col.columnId, col.sortAsc);
thisGrid.grid.customSort(args);
});
};
// Trigger
thisGrid.grid.setInitialSortColumn("dateDue", true);
I can't leave comments due to reputation, which is where this would be most appropriate, however, my answer is in regard to #Premshankar Tiwari and #Siddharth answers.
I preferred the dataView.fastSort option in Siddharth's answer, which works for me in all browsers except IE7 and 8. I didn't test it in IE9 or above. Unfortunately, most on my network run IE7 or 8 due to compatibility issues for legacy applications. BUT, Premshankar's answer works in IE7 and 8.
So, I ended up doing something like this:
if (msie > 0) {
$(".slick-header-columns").children().eq(5).trigger("click");
$(".slick-header-columns").children().eq(4).trigger("click");
} else {
dataView.fastSort('process','unit');
}
where column index (5) = 'unit' and column index (4) = 'process'. Notice that is the reverse order in dataView.fastSort method. I am also using a function that detects IE browser version and assigns it to msie.
My only complaint about utilizing the .trigger method is that if you set up your grid to dynamically hide/show columns, the indexed feature would potentially sort on unintended columns unless you are only calling it on initialization when hide/show capabilities are present.
Maybe it will help you. Looks like SlickGrid is triggering sort to self - so You can trigger it manually if You want.
I'm using multicolumn sorting, and loading saved sort data when initialising the grid.
As expected, setSortColumns set the sorting, but didnt actually apply it, and dataView.reSort() or .fastSort() didnt seem to help, regardless of what point in loading I called them
(I must have missed something, but just couldnt get it to work).
In the end, this worked for me. I call it immediately after populating my dataView from an ajax call.
Its probably not the slickest, so happy to take feedback on board!
function forceResort() {
var sortColumns = grid.getSortColumns();
var cols = [];
$.each(sortColumns, function(index, value) {
var columnId = value.columnId;
var sortAsc = value.sortAsc;
var sortCol = { field: columnId };
var col = { sortCol: sortCol, sortAsc : sortAsc};
cols.push(col);
});
dataView.sort(function (dataRow1, dataRow2) {
var sortResult = 0;
for (var i = 0, l = cols.length; i < l; i++) {
if (sortResult !== 0) {
break;
}
var field = cols[i].sortCol.field;
var sign = cols[i].sortAsc ? 1 : -1;
var value1 = dataRow1[field] || ''; //handle nulls - otherwise erratic sorting
var value2 = dataRow2[field] || ''; //handle nulls - otherwise erratic sorting
if ($.inArray(field, dateTypeColumns) > -1) {
sortResult = compareDates(value1, value2) * sign;
} else {
if ($.inArray(field, numericColumns) > -1) {
sortResult = compareSimple(value1, value2) * sign;
} else {
sortResult = compareAlphaNumeric(value1, value2) * sign;
}
}
}
return sortResult;
});
grid.invalidate();
grid.render();
}
A more clean solution is not to rely on the arguments to onSort but call getSortColumns instead:
function gridSorter() {
var scol=grid.getSortColumns();
if (scol.length===0) return;
var scolId=scol[0].columnId, asc=scol[0].sortAsc;
data.sort(function(a, b) {
var result = a[scolId] > b[scolId] ? 1 : a[scolId] < b[scolId] ? -1 : 0;
return asc ? result : -result;
});
grid.invalidate();
}
Then do:
grid.onSort.subscribe(gridSorter);
This will allow to reestablish sorting anytime you want (from example after reloading the data with ajax) simply by calling gridSorter()
If you want multiple column sorting:
function grid_sorter(args, grid, dataView) {
let cols = args.sortCols;
console.log(cols)
dataView.sort(function (dataRow1, dataRow2) {
for (let i = 0, l = cols.length; i < l; i++) {
let field = cols[i].sortCol.field;
let sign = cols[i].sortAsc ? 1 : -1;
let value1 = dataRow1[field], value2 = dataRow2[field];
let result = (value1 === value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign;
if (result !== 0) {
return result;
}
}
return 0;
});
grid.invalidate();
grid.render();
}
grid_sorter(default_sorting, grid_2, dataView_2);
cols is an object like this:
- sortCols {
- length: 2
- 0 : {
"sortAsc: true,
"sortCol": {
"field: column_id
}
}
- 1: {..}
}

Column sort reset of ascending/descending on 3rd click

Suppose we've a grid with sortable columns.
If a user clicks on a column, it gets sorted by 'asc', then if a user clicks the column header again, it gets sorted by 'desc', now I want similar functionality when a user clicks the column third time, the sorting is removed, i.e. returned to previous style, the css is changed back to normal/non-italic etc?
Today I was trying to achieve same thing. I've skimmed through SlickGrid code but didn't find any 'Reset' function. So, I've tweaked the slick.grid.js v2.2 a little bit.
Basically you just need to add a new array - 'latestSortColumns' which will store sort columns state (deep copy of internal SlickGrid sortColumns array).
var latestSortColumns = [];
Optionally add new setting to opt-in for sort reset.
var defaults = {
(...),
autoResetColumnSort: false
};
Change setupColumnSort to reset sort after third click on column's header.
function setupColumnSort() {
$headers.click(function (e) {
// temporary workaround for a bug in jQuery 1.7.1 (http://bugs.jquery.com/ticket/11328)
e.metaKey = e.metaKey || e.ctrlKey;
if ($(e.target).hasClass("slick-resizable-handle")) {
return;
}
var $col = $(e.target).closest(".slick-header-column");
if (!$col.length) {
return;
}
var column = $col.data("column");
if (column.sortable) {
if (!getEditorLock().commitCurrentEdit()) {
return;
}
var sortOpts = null;
var i = 0;
for (; i < sortColumns.length; i++) {
if (sortColumns[i].columnId == column.id) {
sortOpts = sortColumns[i];
sortOpts.sortAsc = !sortOpts.sortAsc;
break;
}
}
**if ((e.metaKey || (options.autoResetColumnSort && latestSortColumns[i] != null && latestSortColumns[i].sortAsc === !column.defaultSortAsc)) && options.multiColumnSort) {**
if (sortOpts) {
sortColumns.splice(i, 1);
**latestSortColumns.splice(i, 1);**
}
}
else {
if ((!e.shiftKey && !e.metaKey) || !options.multiColumnSort) {
sortColumns = [];
}
if (!sortOpts) {
sortOpts = { columnId: column.id, sortAsc: column.defaultSortAsc };
sortColumns.push(sortOpts);
} else if (sortColumns.length == 0) {
sortColumns.push(sortOpts);
}
}
setSortColumns(sortColumns);
if (!options.multiColumnSort) {
trigger(self.onSort, {
multiColumnSort: false,
sortCol: column,
sortAsc: sortOpts.sortAsc}, e);
} else {
trigger(self.onSort, {
multiColumnSort: true,
sortCols: $.map(sortColumns, function(col) {
return {sortCol: columns[getColumnIndex(col.columnId)], sortAsc: col.sortAsc };
})}, e);
}
}
});
}
Store new state after every sort change in latestSortColumns:
function setSortColumns(cols) {
sortColumns = cols;
var headerColumnEls = $headers.children();
headerColumnEls
.removeClass("slick-header-column-sorted")
.find(".slick-sort-indicator")
.removeClass("slick-sort-indicator-asc slick-sort-indicator-desc");
$.each(sortColumns, function(i, col) {
if (col.sortAsc == null) {
col.sortAsc = true;
}
var columnIndex = getColumnIndex(col.columnId);
if (columnIndex != null) {
headerColumnEls.eq(columnIndex)
.addClass("slick-header-column-sorted")
.find(".slick-sort-indicator")
.addClass(col.sortAsc ? "slick-sort-indicator-asc" : "slick-sort-indicator-desc");
}
});
**for (var i = 0; i < sortColumns.length; i++)
latestSortColumns[i] = { columnId: sortColumns[i].columnId, sortAsc: sortColumns[i].sortAsc };**
}
That's all, should work now.
This is a function I call to reset all columns back to their original order.
It requires one of columns to be set up as not sortable.
In the example below I use the first column of the table, columns[0], which has the field id "locus".
function removeSorting() {
columns[0].sortable = true;
$('.slick-header-columns').children().eq(0).trigger('click');
columns[0].sortable = false;
// clear other sort columns
grid.setSortColumns( new Array() );
}
Then in the typical dataView.sort() function you make an exception for this column:
grid.onSort.subscribe(function(e,args) {
var cols = args.sortCols;
dataView.sort(function (dataRow1, dataRow2) {
for( var i = 0; i < cols.length; i++ ) {
var field = cols[i].sortCol.field;
// reset sorting to original indexing
if( field === 'locus' ) {
return (dataRow1.id > dataRow2.id) ? 1 : -1;
}
var value1 = dataRow1[field];
var value2 = dataRow2[field];
if( value1 == value2 ) continue;
var sign = cols[i].sortAsc ? 1 : -1;
return (value1 > value2) ? sign : -sign;
}
return 0;
});
});
Will the table always be sorted in some order on some column?
If not then you'll have to store a lot of state the first time a column is clicked just in case you want to restore it after a third click.

Count the number of "holes" in a bitmap

Consider a MxN bitmap where the cells are 0 or 1. '1' means filled and '0' means empty.
Find the number of 'holes' in the bitmap, where a hole is a contiguous region of empty cells.
For example, this has two holes:
11111
10101
10101
11111
... and this has only one:
11111
10001
10101
11111
What is the fastest way, when M and N are both between 1 and 8?
Clarification: diagonals are not considered contiguous, only side-adjacency matters.
Note: I am looking for something that takes advantage of the data format. I know how to transform this into a graph and [BD]FS it but that seems overkill.
You need to do connected component labeling on your image. You can use the Two-pass algorithm described in the Wikipedia article I linked above. Given the small size of your problem, the One-pass algorithm may suffice.
You could also use BFS/DFS but I'd recommend the above algorithms.
This seems like a nice use of the disjoint-set data structure.
Convert the bitmap to a 2d array
loop through each element
if the current element is a 0, merge it with the set of one its 'previous' empty neighbors (already visited)
if it has no empty neighbors, add it to its own set
then just count the number of sets
There may be advantages gained by using table lookups and bitwise operations.
For example whole line of 8 pixels may be looked up in 256 element table, so number of holes in a field 1xN is got by single lookup. Then there may be some lookup table of 256xK elements, where K is number of hole configurations in previous line, contatining number of complete holes and next hole configuration. That's just an idea.
I wrote an article describe the answer on Medium https://medium.com/#ahmed.wael888/bitmap-holes-count-using-typescript-javascript-387b51dd754a
but here is the code, the logic isn't complicated and you can understand it without reading the article.
export class CountBitMapHoles {
bitMapArr: number[][];
holesArr: Hole[] = [];
maxRows: number;
maxCols: number;
constructor(bitMapArr: string[] | number[][]) {
if (typeof bitMapArr[0] == 'string') {
this.bitMapArr = (bitMapArr as string[]).map(
(word: string): number[] => word.split('').map((bit: string): number => +bit))
} else {
this.bitMapArr = bitMapArr as number[][]
}
this.maxRows = this.bitMapArr.length;
this.maxCols = this.bitMapArr[0].length;
}
moveToDirection(direction: Direction, currentPosition: number[]) {
switch (direction) {
case Direction.up:
return [currentPosition[0] - 1, currentPosition[1]]
case Direction.down:
return [currentPosition[0] + 1, currentPosition[1]]
case Direction.right:
return [currentPosition[0], currentPosition[1] + 1]
case Direction.left:
return [currentPosition[0], currentPosition[1] - 1]
}
}
reverseDirection(direction: Direction) {
switch (direction) {
case Direction.up:
return Direction.down;
case Direction.down:
return Direction.up
case Direction.right:
return Direction.left
case Direction.left:
return Direction.right
}
}
findNeighbor(parentDir: Direction, currentPosition: number[]) {
let directions: Direction[] = []
if (parentDir === Direction.root) {
directions = this.returnAvailableDirections(currentPosition);
} else {
this.holesArr[this.holesArr.length - 1].positions.push(currentPosition)
directions = this.returnAvailableDirections(currentPosition).filter((direction) => direction != parentDir);
}
directions.forEach((direction) => {
const childPosition = this.moveToDirection(direction, currentPosition)
if (this.bitMapArr[childPosition[0]][childPosition[1]] === 0 && !this.checkIfCurrentPositionExist(childPosition)) {
this.findNeighbor(this.reverseDirection(direction), childPosition)
}
});
return
}
returnAvailableDirections(currentPosition: number[]): Direction[] {
if (currentPosition[0] == 0 && currentPosition[1] == 0) {
return [Direction.right, Direction.down]
} else if (currentPosition[0] == 0 && currentPosition[1] == this.maxCols - 1) {
return [Direction.down, Direction.left]
} else if (currentPosition[0] == this.maxRows - 1 && currentPosition[1] == this.maxCols - 1) {
return [Direction.left, Direction.up]
} else if (currentPosition[0] == this.maxRows - 1 && currentPosition[1] == 0) {
return [Direction.up, Direction.right]
} else if (currentPosition[1] == this.maxCols - 1) {
return [Direction.down, Direction.left, Direction.up]
} else if (currentPosition[0] == this.maxRows - 1) {
return [Direction.left, Direction.up, Direction.right]
} else if (currentPosition[1] == 0) {
return [Direction.up, Direction.right, Direction.down]
} else if (currentPosition[0] == 0) {
return [Direction.right, Direction.down, Direction.left]
} else {
return [Direction.right, Direction.down, Direction.left, Direction.up]
}
}
checkIfCurrentPositionExist(currentPosition: number[]): boolean {
let found = false;
return this.holesArr.some((hole) => {
const foundPosition = hole.positions.find(
(position) => (position[0] == currentPosition[0] && position[1] == currentPosition[1]));
if (foundPosition) {
found = true;
}
return found;
})
}
exec() {
this.bitMapArr.forEach((row, rowIndex) => {
row.forEach((bit, colIndex) => {
if (bit === 0) {
const currentPosition = [rowIndex, colIndex];
if (!this.checkIfCurrentPositionExist(currentPosition)) {
this.holesArr.push({
holeNumber: this.holesArr.length + 1,
positions: [currentPosition]
});
this.findNeighbor(Direction.root, currentPosition);
}
}
});
});
console.log(this.holesArr.length)
this.holesArr.forEach(hole => {
console.log(hole.positions)
});
return this.holesArr.length
}
}
enum Direction {
up = 'up',
down = 'down',
right = 'right',
left = 'left',
root = 'root'
}
interface Hole {
holeNumber: number;
positions: number[][]
}
main.ts file
import {CountBitMapHoles} from './bitmap-holes'
const line = ['1010111', '1001011', '0001101', '1111001', '0101011']
function main() {
const countBitMapHoles = new CountBitMapHoles(line)
countBitMapHoles.exec()
}
main()
function BitmapHoles(strArr) {
let returnArry = [];
let indexOfZ = [];
let subarr;
for(let i=0 ; i < strArr.length; i++){
subarr = strArr[i].split("");
let index = [];
for(let y=0 ; y < subarr.length; y++){
if(subarr[y] == 0)
index.push(y);
if(y == subarr.length-1)
indexOfZ.push(index);
}
}
for(let i=0 ; i < indexOfZ.length; i++){
for(let j=0; j<indexOfZ[i].length ; j++){
if(indexOfZ[i+1] && (indexOfZ[i][j]==indexOfZ[i+1][j] || indexOfZ[i+1].indexOf(indexOfZ[i][j])))
returnArry.indexOf(strArr[i]) < 0 ? returnArry.push(strArr[i]): false;
if(Math.abs(indexOfZ[i][j]-indexOfZ[i][j+1])==1)
returnArry.indexOf(strArr[i]) < 0 ? returnArry.push(strArr[i]): false;
}
}
return returnArry.length;
}
// keep this function call here
console.log(BitmapHoles(readline()));
function findHoles(map) {
let hole = 0;
const isHole = (i, j) => map[i] && map[i][j] === 0;
for (let i = 0; i < map.length; i++) {
for (let j = 0; j < map[i].length; j++) {
if (isHole(i, j)) {
markHole(i, j);
hole++;
}
}
}
function markHole(i, j) {
if (isHole(i, j)) {
map[i][j] = 2;
markHole(i, j - 1);
markHole(i, j + 1);
markHole(i + 1, j);
markHole(i - 1, j);
}
}
return hole;
}

Resources