Detect free space in two dimension array - javascript

I need this for angular gridster when I add new item so I know the dimension of the new element I'm adding (when there is no space for current element), but to simplify lets assume that I have 2 dimension array with value true or false and I want to search the first free space in array to find position x,y and width,height of free space. So far I have this:
var array = [
[false, false, false, false, false, false],
[false, false, false, false, false, false],
[false, false, false, false, false, false],
[false, false, false, true, true, true],
[false, false, false, true, true, true]
];
var place = {};
loop:
for (var i=0; i<array.length; i++) {
for (var j=0; j<array[i].length; j++) {
if (array[i][j] && !place.x && !place.y) {
place.x = j;
place.y = i;
place.width = 0;
place.height = 0;
for (var y=i; y<array.length; y++) {
for (var x=j; x<array[y].length; x++) {
if (array[y][x]) {
place.width = x - j + 1;
place.height = y - i + 1;
}
}
}
break loop;
}
}
}
console.log(place);
but this will fail for array like this:
var array = [
[false, false, false, false, false],
[false, false, false, false, false],
[false, false, false, false, false],
[true, true, false, true, true],
[true, true, false, true, true]
];
How can I fix my code to make it work for array like this? The result should be:
{x:0, y:3, width: 2, height: 2}

Here's another option. I tried to divide the problem in smaller ones:
Find the first true element in the matrix
From there, find the number of true elements adjacent to 1
Here's the fiddle and here's the code. Hope it helps.
function findCoords(matrix) {
for (var row = 0; row < matrix.length; row++) {
for (var col = 0; col < matrix[row].length; col++) {
if (matrix[row][col]) {
return createCoordsObj(row, col, matrix);
}
}
}
return null;
}
function createCoordsObj(row, col, matrix) {
return {
x: col,
y: row,
width: find('width', row, col, matrix),
height: find('height', row, col, matrix)
};
}
function find(type, row, col, matrix) {
var res = 0;
while (matrix[row] && matrix[row][col]) { // will finish when element in matrix is false || undefined
res += 1;
col += type === 'width' ? 1 : 0;
row += type === 'width' ? 0 : 1;
}
return res;
}
console.log(findCoords([
[false, false, false, false, false],
[false, false, false, false, false],
[false, false, false, false, false],
[true, true, false, true, true],
[true, true, false, true, true]
]));

I guess you might do as follows; It will return you the x and y coordinates of the upper left corner along with the width and height of the free opening. If there is no opening (all false) you will be returned a false.
var array = [
[false, false, false, false, false],
[false, false, false, false, false],
[false, false, true, true, true],
[false, false, true, true, true],
[false, false, true, true, true]
],
emptyXY = (a) => { var x,
y = a.findIndex(row => (x = row.findIndex(col => col), x !== -1));
w = 0,
h = 0;
while (a[y] && a[y][x+w]) w++;
while (a[y+h] && a[y+h][x]) h++;
return !!~y && {x:x,y:y,width:w,height:h};
};
console.log(emptyXY(array));

Related

Make variable accessible through whole function

I have the following function:
var handsonTableHandler = (function () {
var container = document.getElementById('WebGrid');
var hot = "";
var init = function () {
//container is null here, why?
Handsontable.renderers.registerRenderer('dataStyling', dataStyling);
hot = new Handsontable(container, {
startRows: 18,
startCols: 24,
autoWrapRow: true,
width: 1400,
height: 441,
rowHeaders: true,
colHeaders: true,
outsideClickDeselects: false,
search: true,
manualColumnResize: true,
stretchH: "all",
afterChange: function (source, changes) {},
cells: function (row, col, prop) {
var cellProperties = {};
cellProperties.renderer = "dataStyling"; // uses lookup map
cellProperties;
}
});
}
}
$(document).ready(function() {
handsonTableHandler.init();
});
I want the container and hot variable to be accessible through the whole function, thus, in the init function and other functions that are defined here.
As you can see, I'm getting the element for with the Id WebGrid. But in the init-function, it's null. Why is this?
If it was not accessable in the inner function, it would be "Undefined" instead of "Null", therefore it is accessable
you did
var container = document.getElementById('WebGrid');
but document.getElementById(...) returns Null if the element does not exist, therefore there mustnt have been an element with the id of WebGrid
Try in the console and type
document.getElementById('WebGrid')
if it returns null, then there was no element in the first place
now change your code to:
$(document).ready(function() {
var handsonTableHandler = (function () {
var container = document.getElementById('WebGrid');
var hot = "";
var init = function () {
//container is null here, why?
Handsontable.renderers.registerRenderer('dataStyling', dataStyling);
hot = new Handsontable(container, {
startRows: 18,
startCols: 24,
autoWrapRow: true,
width: 1400,
height: 441,
rowHeaders: true,
colHeaders: true,
outsideClickDeselects: false,
search: true,
manualColumnResize: true,
stretchH: "all",
afterChange: function (source, changes) {},
cells: function (row, col, prop) {
var cellProperties = {};
cellProperties.renderer = "dataStyling"; // uses lookup map
cellProperties;
}
});
};
})();
handsonTableHandler.init();
});
The only to fix this, is to assign the DOM element to container in init, like this:
var handsonTableHandler = (function () {
var container,
hot;
var init = function () {
container = document.getElementById('WebGrid');
//container is null here, why?
Handsontable.renderers.registerRenderer('dataStyling', dataStyling);
hot = new Handsontable(container, {
startRows: 18,
startCols: 24,
autoWrapRow: true,
width: 1400,
height: 441,
rowHeaders: true,
colHeaders: true,
outsideClickDeselects: false,
search: true,
manualColumnResize: true,
stretchH: "all",
afterChange: function (source, changes) {},
cells: function (row, col, prop) {
var cellProperties = {};
cellProperties.renderer = "dataStyling"; // uses lookup map
cellProperties;
}
});
}
})()
You can change you function to create handler with new syntax and get access to .init function
let handsonTableHandler = function () {
this.container = document.getElementById('WebGrid');
this.hot = "";
this.init = function () {
console.log('container', this.container);
Handsontable.renderers.registerRenderer('dataStyling', dataStyling);
this.hot = new Handsontable(this.container, {
startRows: 18,
startCols: 24,
autoWrapRow: true,
width: 1400,
height: 441,
rowHeaders: true,
colHeaders: true,
outsideClickDeselects: false,
search: true,
manualColumnResize: true,
stretchH: "all",
afterChange: function (source, changes) {},
cells: function (row, col, prop) {
var cellProperties = {};
cellProperties.renderer = "dataStyling"; // uses lookup map
cellProperties;
}
});
}
}
let handler = new handsonTableHandler();
$(document).ready(function() {
handler.init(); // output: container not null
});
to me it seems you are not running the function so the closure is never created, I added a few parenthesis, this should create the closure:
var handsonTableHandler = function () {
var container = document.getElementById('WebGrid');
var hot = "";
var init = function () {
//container is null here, why?
Handsontable.renderers.registerRenderer('dataStyling', dataStyling);
hot = new Handsontable(container, {
startRows: 18,
startCols: 24,
autoWrapRow: true,
width: 1400,
height: 441,
rowHeaders: true,
colHeaders: true,
outsideClickDeselects: false,
search: true,
manualColumnResize: true,
stretchH: "all",
afterChange: function (source, changes) {},
cells: function (row, col, prop) {
var cellProperties = {};
cellProperties.renderer = "dataStyling"; // uses lookup map
cellProperties;
}
});
}
return {init: init}
};
$(document).ready(function() {
handsonTableHandler().init();
});

Freeze columns after move

I would like to freeze two columns after I move them.
ex: JSFiddle
var
myData = Handsontable.helper.createSpreadsheetData(10, 50),
container = document.getElementById('example1'),
hot;
hot = new Handsontable(container, {
data: myData,
rowHeaders: false,
colHeaders: true,
preventOverflow: 'horizontal',
allowInsertRow: false,
allowInsertColumn: false,
fixedColumnsLeft: 2,
contextMenu: false,
manualColumnMove: [2, 5],
manualColumnFreeze: true
});
But it's possible to move them again after manually...
How can I block the manual move after I move them in option ?
Or just freeze the fixed column ?
Thanks guys ;)
I finally find how to freeze column, but the guide is always displaying.
Solution : JSFiddle
var
myData = Handsontable.helper.createSpreadsheetData(10, 50),
container = document.getElementById('example1'),
hot,
fixedColumnsLeft = 2;
hot = new Handsontable(container, {
data: myData,
rowHeaders: false,
colHeaders: true,
preventOverflow: 'horizontal',
allowInsertRow: false,
allowInsertColumn: false,
fixedColumnsLeft: fixedColumnsLeft,
contextMenu: false,
manualColumnMove: true,
manualColumnFreeze: true,
beforeColumnMove: setBeforeColumnMove(),
});
function setBeforeColumnMove() {
return function(startColumn, endColumn) {
var manualColumnMove = hot.getPlugin("ManualColumnMove");
if (startColumn < fixedColumnsLeft || endColumn < fixedColumnsLeft) {
manualColumnMove.changeColumnPositions(endColumn, startColumn);
}
}
};

Aasorting in jQuery based on filters

I have a datatable style in which I want to disable/enable the initial sort based on some filters in aspx.
The setting has a aasorting property, lets assume I have a global variable "isDefaultSortingEnabled" and based on this variable I want to perform the sorting. I tried using if-else, but we cant write it inside the style setting.
var objDataTableSettings = {
"bPaginate": false,
"bFilter": false,
"aaSorting": [] , // manipulate this sorting based on a global variable
// "aaSorting": [[1, 'asc']],
"bProcessing": true,
"aoColumnDefs": [
You can use a ternary expression within the object to set the aaSorting property based on your global variable. Try this:
var objDataTableSettings = {
"bPaginate": false,
"bFilter": false,
"bProcessing": true,
'aaSorting': isDefaultSortingEnabled ? [] : [[ 1, 'asc' ]];
// other settings...
}
If you prefer to use a full if/else statement, you would need to first create the object, then change the property as required:
var objDataTableSettings = {
"bPaginate": false,
"bFilter": false,
"bProcessing": true,
// other settings...
}
if (isDefaultSortingEnabled) {
objDataTableSettings.aaSorting = [];
} else {
objDataTableSettings.aaSorting = [[ 1, 'asc' ]];
}
The former is preferred due to its brevity.
if (isDefaultSortingEnabled) {
aaSortingdData = [];
} else {
aaSortingData = [[ 1, 'asc' ]];
}
var objDataTableSettings = {
"bPaginate": false,
"bFilter": false,
"bProcessing": true,
"aaSorting": aaSortingData
}

Retain filter,sort,expanded,page,selection from jqgrid after reload

I need to refresh data from server in jqgrid automatically for 10 minutes interval .
After refresh i need to retail filter,sorting,subgrid expanded, page, selected row(multiple).
I am able to retain all except page and selected row. In this i can able to do either page or selected row only. Cant able to retain both. Only one works for me.
Code
jQuery("#tblCompletedPriceList").jqGrid({
url: $('#tblCompletedPriceList').attr("RequestUrl"),
datatype: "json",
mtype: "Get",
hoverrows: false,
colNames: CompletedColName,
colModel: CompletedColModel,
id: 'PriceListID',
localReader: { id: 'PriceListID' },
prmNames: { id: "PriceListID" },
rowNum: 10,
hidegrid: false,
rownumbers: true,
pager: '#CompletedPriceListGridPager',
viewrecords: true,
caption: "Completed Price List Queue",
height: 'auto',
scrollOffset: 0,
gridview: true,
shrinkToFit: true,
autoencode: true,
loadonce: true,
ignoreCase: true,
multiselect: true,
subGrid: true,
enableClear: true,
loadComplete: function () {
UnBlockUI();
var $this = $(this);
var selRowIds = jQuery('#tblCompletedPriceList').jqGrid('getGridParam', 'selarrow');
postfilt = $this.jqGrid('getGridParam', 'postData').filters;
postsord = $this.jqGrid('getGridParam', 'postData').sord;
postsort = $this.jqGrid('getGridParam', 'postData').sidx;
postpage = $this.jqGrid('getGridParam', 'postData').page;
var selRowIds = jQuery('#tblCompletedPriceList').jqGrid('getGridParam', 'selarrrow');
if ($this.jqGrid("getGridParam", "datatype") === "json") {
setTimeout(function () {
$this.jqGrid("setGridParam", {
datatype: "local",
postData: { filters: postfilt, sord: postsord, sidx: postsort },
search: true
});
$this.trigger("reloadGrid", [{ page: postpage }]);
}, 15);
}
if (expandedids != "") {
$("#tblCompletedPriceList").jqGrid('expandSubGridRow', expandedids);
expandedids = "";
}
if (selRowIds.length < 1) {
return false;
} else {
var i, count;
for (i = 0, count = selRowIds.length; i < count; i++) {
jQuery('#tblCompletedPriceList').jqGrid('setSelection', selRowIds[i], false);
}
}
},
beforeRequest: function () {
$("#tblCompletedPriceList tr:has(.sgexpanded)").each(function () {
expandedids = $(this).attr('id');
});
}
});
To reload
window.setTimeout(refreshGrid, 600000);
function refreshGrid() {
$('#tblCompletedPriceList').setGridParam({ datatype: 'json' }).trigger("reloadGrid");
window.setTimeout(refreshGrid, 600000);
}

Iterating an array

I have framed an array like below
iArray = [true, true, false, false, false, false, false, false, true, true, true, false,
true, false, false, false, false, true]
Condtional check:
If anyone of the value in this array is false I will be showing an error message
else if everything is true I will be showing success message.
I tired below code to iterate, however couldn't frame the logic in it.
var boolIteration = iArray.split(',');
var i;
for (i = 0; i < boolIteration.length; ++i) {
//conditional check
}
I'm struggling to iterate the array using the above condition.
Can anyone point me in the right direction with an efficient solution.
No need for jQuery
if (iArray.indexOf(false) !== -1) {
// error
}
Also, as previous commenters have already pointed out, iArray is already an array, there's no need to use split on it.
The Array.prototype.indexOf is not available in Internet Explorer below 9. However, this functionality could be easily added with a matching algorithm. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf for compatibility and how to create a workaround.
iArray is already an array, so there is no need to split it again (split is a method for String, Arrays don't have it)
What you need to do is check the index of a false value, if it is there then there is a false value in the array.
using jQuery - the array indexOf is not used because of IE compatibility
iArray = [true, true, false, false, false, false, false, false, true, true, true, false,
true, false, false, false, false, true]
if($.inArray(false, iArray ) != -1){
//error
}
var iArray = [true, true, false, false, false, false, false, false, true, true,
true, false,true, false, false, false, false, true];
for (var i = 0; i < iArray.length; i++) {
if (iArray[i]) {
alert("success");
}
else {
alert("error");
}
}
Aternatives:
if (/false/i.test(iArray)) { }
or
if ( ''.replace.call(iArray,/true|,/g,'').length ) { }
The jQuery.inArray function is nice for checking to see if a particular value is contained within an array.
iArray = [true, true, false, false, false, false, false, false, true, true, true, false, true, false, false, false, false, true];
if ($.inArray(iArray, false) >= 0) {
// the value "false" is contained within the array, show an error message
}

Categories