I wrote the code below to search for values in a spreadsheet. For some reason, when I try to search vertically it searches horizontally instead.
I thought that changing valores[cc][0] to valores[0][cc] would do that but it's not working.
Any idea what I am doing wrong?
function onEdit(e){
var a = e.source.getActiveSheet();
var SearchText = "4"
//x = mainSearch( a, 3, 1, "horizontal", SearchText);
x = mainSearch( a, 1, 1, "vertical", SearchText);
}
//mainSearch( targetSheet, row, column, alignment, searchText)
function mainSearch( folha, linha, coluna, procTipo, procTexto) {
if ( procTipo = "horizontal" ) {
var alcance = folha.getRange( linha, coluna, folha.getLastRow(), 1);
}
else if ( procTipo = "vertical" ) {
var alcance = folha.getRange( linha, coluna, 1, folha.getLastColumn());
}
else {
Browser.msgBox("mainSerch com procTipo errado");
}
var valores = alcance.getValues();
for(cc=0;cc<valores.length;++cc) {
if ( procTipo = "horizontal" ) {
Browser.msgBox("Horizontal --> " + valores[cc][0]);
if ( valores[cc][0] == procTexto ) {
return (cc + linha);
}
}
else if ( procTipo = "vertical" ) {
Browser.msgBox("Vertical --> " + valores[0][cc]);
if ( valores[0][cc] == procTexto ) {
return (cc + coluna);
}
}
}
return 0;
}
The problem is here:
if ( procTipo = "horizontal" ) {
When you execute procTipo = "horizontal", you're assigning "horizontal" to procTipo. You should only test its value:
if ( procTipo == "horizontal" ) {
There are three other place where you'll have to change = to ==.
Some people prefer to use === because it doesn't do any type coercion, but in this situation == will work equally well.
You'll have to adjust the iteration limit in order to search through valores properly in the vertical case. Currently you have this:
for(cc=0;cc<valores.length;++cc) {
Replace it with these two lines:
var limit = (procTipo == 'horizontal' ? valores.length : valores[0].length);
for (var cc = 0; cc < limit; ++cc) {
Related
The code below has been working, but I'm afraid that with a couple of thousands rows more, it can get laggy and I was wondering what would be the approach to improve it.
It basically compares 2 datasets by a number common to both and update a couple of columns with their status/checkboxes (TRUE, or FALSE):
function onEdit(e) {
if (e.range.getSheet().getName() === 'Todays Tests V2' && e.range.getA1Notation() === 'C3') {
var formRespSheet = e.source.getSheetByName('Form Responses 1');
var formRespRng = formRespSheet.getRange(2, 13, formRespSheet.getLastRow() - 1, 4);
var formRespValues = formRespRng.getValues();
var todaysTest = e.source.getSheetByName('Todays Tests V2');
var todaysTestData = todaysTest.getRange(6, 1, todaysTest.getLastRow(), 18).getValues();
todaysTest.getRange('O6:O').clearContent();
todaysTest.getRange('Q6:Q').clearContent();
todaysTest.getRange('R6:R').clearContent();
for (var i = 0; i < formRespValues.length; i++) {
for (var j = 0; j < todaysTestData.length; j++)
if (formRespValues[i][1] == todaysTestData[j][1]) {
if (formRespValues[i][0] == 'Yes') {
todaysTest.getRange('O' + (6 + j)).setValue('TRUE')
} else {
todaysTest.getRange('O' + (6 + j)).setValue('FALSE')
}
if (formRespValues[i][2] == 'Yes') {
todaysTest.getRange('Q' + (6 + j)).setValue('TRUE')
} else {
todaysTest.getRange('Q' + (6 + j)).setValue('FALSE')
}
if (formRespValues[i][3] == 'Yes') {
todaysTest.getRange('R' + (6 + j)).setValue('TRUE')
} else {
todaysTest.getRange('R' + (6 + j)).setValue('FALSE')
}
}
}
I see some things that can be further improved with #MisterJojo's answer, see modifications below:
Modifications:
function onEdit(e) {
if (e.range.getSheet().getName() === 'Todays Tests V2' &&
e.range.getA1Notation() === 'C3') {
let formRespSheet = e.source.getSheetByName('Form Responses 1'),
formRespRng = formRespSheet.getRange(2, 13, formRespSheet.getLastRow() - 1, 4),
formRespValues = formRespRng.getValues(),
todaysTest = e.source.getSheetByName('Todays Tests V2'),
todaysTestNumRows = todaysTest.getLastRow() - 5,
todaysTestData = todaysTest.getRange(6, 1, todaysTestNumRows, 18).getValues();
// this can be replaced with getValues if it feels sluggish
var inputB = todaysTestData.map(x => x[1]),
outputO = todaysTestData.map(x => [x[14]]),
outputQR = todaysTestData.map(x => [x[16], x[17]]);
for (const frv of formRespValues) {
let frv0test = (frv[0] == 'Yes') ? 'TRUE' : 'FALSE',
frv2test = (frv[2] == 'Yes') ? 'TRUE' : 'FALSE',
frv3test = (frv[3] == 'Yes') ? 'TRUE' : 'FALSE';
let index = inputB.indexOf(frv[1]);
if (index > -1) {
outputO[index] = [frv0test];
outputQR[index] = [frv2test, frv3test];
}
}
// write by bulk
todaysTest.getRange(6, 15, outputO.length, outputO[0].length).setValues(outputO);
todaysTest.getRange(6, 17, outputQR.length, outputQR[0].length).setValues(outputQR);
}
}
Summary of changes:
Fixed the incorrect last row value of todaysTestData by subtracting 5.
Removed clearContent since you are writing FALSE after a blank cell which basically removes the tick in the checkbox.
Used map instead to reduce method calls to SpreadsheetApp. These map functions can be replaced by getValues if you see a better getValues performance.
Instead of looping your todaysTestData, just use indexOf to check what index frv[1] is found on the columnB of todaysTestData (if there is any) and use that index to write data there.
Write by bulk using setValues instead of looping setValue.
Combine writing Q and R since they are adjacent columns.
try that...
function onEdit(e)
{
if ( e.range.getSheet().getName() === 'Todays Tests V2'
&& e.range.getA1Notation() === 'C3')
{
let
formRespSheet = e.source.getSheetByName('Form Responses 1')
, formRespRng = formRespSheet.getRange(2, 13, formRespSheet.getLastRow() - 1, 4)
, formRespValues = formRespRng.getValues()
, todaysTest = e.source.getSheetByName('Todays Tests V2')
, todaysTestData = todaysTest.getRange(6, 1, todaysTest.getLastRow(), 18).getValues()
;
todaysTest.getRange('O6:O').clearContent();
todaysTest.getRange('Q6:Q').clearContent();
todaysTest.getRange('R6:R').clearContent();
for (const frv of formRespValues)
{
let
frv0test = (frv[0] == 'Yes') ? 'TRUE' : 'FALSE'
, frv2test = (frv[2] == 'Yes') ? 'TRUE' : 'FALSE'
, frv3test = (frv[3] == 'Yes') ? 'TRUE' : 'FALSE'
;
todaysTestData.forEach( (ttd,j) =>
{
if (frv[1] == ttd[1])
{
let j6 = j+6
todaysTest.getRange(`O${j6}`).setValue( frv0test )
todaysTest.getRange(`Q${j6}`).setValue( frv2test )
todaysTest.getRange(`R${j6}`).setValue( frv3test )
}
})
}
}
}
I am having trouble trying to alert the winner, among other things. I also am trying to alert when the user trys to click on a button that has already been pressed, but have no idea what I am doing in that regard either. All help is appreciated. Thank you guys.
<table>
<tr>
<td id="00" onclick="makeMove(0,0)">00</td>
<td id ="01" onclick="makeMove(0,1)">01</td>
<td id ="02" onclick="makeMove(0,2)">02</td>
</tr>
<tr>
<td id ="10" onclick="makeMove(1,0)">10</td>
<td id ="11" onclick="makeMove(1,1)">11</td>
<td id ="12" onclick="makeMove(1,2)">12</td>
</tr>
<tr>
<td id ="20" onclick="makeMove(2,0)">20</td>
<td id ="21" onclick="makeMove(2,1)">21</td>
<td id ="22" onclick="makeMove(2,2)">22</td>
</tr>
</table>
<hr>
<input id="myMoveButton" type="submit">
<script src="java.js"></script>
Javascript:
// -1 means 0's turn
// 1 means X's turn
// AFTER EVERY MOVE
// UNTIL GAME OVER
// THIS NEEDS TO BE FLIPPED
// IF IT WAS 1, now it will be -1 and vice versa
var turn = 1;
// 0 means no move has been made yet
// on that particular square
var grid = [
[0, 0 ,0],
[0, 0 ,0],
[0, 0, 0]
];
function makeMove(row, column) {
if (grid [row][column] == 0) {
grid[row][column] = turn;
var idOfSquareToChange = row +"" + column;
if (turn == 1) {
$("#"+idOfSquareToChange).html('X');
}else {
$("#"+idOfSquareToChange).html('O');
}
// CHECK IF GAME IS OVER
// IF GAME ISN'T OVER
if (turn == 1) {
turn = -1;
}else {
turn = 1;
}
printGrid();
}else {
alert('That square has been chosen');
}
}
function printGrid(){
var board = grid[0][0] + " " + grid [0][1] + " " + grid[0][2];
board += "\n";
board += grid[1][0] + " " + grid [1][1] + " " + grid[1][2];
board += "\n";
board += grid[2][0] + " " + grid [2][1] + " " + grid[2][2];
alert(board);
}
function isGameOver() {
// HERE IS WHERE OUR LOGIC WOULD GO
// TO SEE IF SOMEONE won
}
This is what i came up with:
function isGameOver() {
for (var i = 0; i < grid.length; i++) {
if(grid[i][0] == grid[i][1] && grid[i][0]==grid[i][2] && grid[i][0]!=0){
alert(grid[i][0]+" Wins");
_win=1;
return;
}
}
for (var i = 0; i < grid.length; i++) {
if(grid[0][i] == grid[1][i] && grid[0][i]==grid[2][i] && grid[0][i]!=0){
alert(grid[0][i]+" Wins");
_win=1;
return;
}
}
if(grid[0][0]==grid[1][1] && grid[0][0] == grid[2][2] && grid[0][0]!=0){
alert(grid[0][0]+" Wins");
_win=1;
return;
}
if(grid[0][2]==grid[1][1] && grid[0][2] == grid[2][0] && grid[2][0]!=0){
alert(grid[1][1]+" Wins");
_win=1;
return;
}
}
Working fiddle
This will check whether data in a single column or in a single row or in diagonals should be same. And if a user wins then he can not click on anything else.
By using lodash, you can abstract a bunch of the pieces of this. I went ahead and re-coded it to show you how I'd do it. This will allow you to make the board whatever size you want and allow for up to 4 players.
Here's a jsfiddle:
https://jsfiddle.net/mckinleymedia/gqqse6cw/18/
Here's all the HTML you need:
<div class="gameboard"></div>
Here's the script:
var game = function( options ){
options = options || [];
var target = options.target || '.gameboard',
size = options.size || [ 3, 3 ],
players = options.players || 2,
toWin = options.toWin || 3,
symbols = [ 'o', 'x', '+', '!' ],
current,
grid,
setup = function ( ) {
$(target).empty();
current = 0;
grid = [];
_.each(_.range(size[0]), function(r) {
var row = $('<div>')
.addClass('row');
grid.push([]),
_.each(_.range(size[1]), function(c) {
grid[r].push(undefined),
row.append(
$('<button>')
.addClass('square open')
.attr('data-pos',r + ',' + c)
.html(' ')
);
})
$(target).append(row );
});
$(target).append(
$('<div>')
.addClass('player')
.append(
$('<span>')
.html('Player')
)
.append(
$('<span>')
.addClass('symbol')
.html(symbols[0])
)
);
$('.square').click( function(){
if (!($(this).hasClass('disabled'))) makeMove(this)
});
},
makeMove = function (btn) {
var btn = $(btn)
.addClass('disabled')
.html(symbols[current]),
pos = btn.attr('data-pos').split(',');
grid[pos[0]][pos[1]] = current;
checkBoard();
},
checkBoard = function () {
var winners = [];
checkArray()
winners = checkArray(grid,winners); // check rows
winners = checkArray(_.zip.apply(_, grid) ,winners,'vert'); // check columns
var diag = [],
diagReverse = [],
i = 0,
di = 0,
map = [],
mapR = [];
_.each(grid, function(g, gk){
_.each(g, function(cell, ck){
di = ck + i;
diag[di] = diag[di] || [];
diagReverse[di] = diagReverse[di] || [];
map[di] = map[di] || [];
mapR[di] = mapR[di] || [];
diag[di].push(grid[gk][ck]);
diagReverse[di].push( grid[ grid.length - gk - 1 ][ ck ]
);
map[di].push([gk,ck]);
mapR[di].push([(grid.length - gk - 1), ck]);
});
i++;
});
winners = checkArray(diag ,winners, map); // check diagonal
winners = checkArray(diagReverse ,winners, mapR); // check reverse diagonal
if ( winners.length > 0 ) playerWin(winners);
current++;
current = current % players;
$(target + ' .symbol')
.html(symbols[current]);
},
checkArray = function (arr,winners,map) {
map = map || 0;
var winner = [],
setRow = [],
count = 0,
cur = -1;
_.each(arr, function(g, key){
count = 0;
_.each(g, function(cell, ckey){
if (cell===undefined || cell !== cur) {
count = 0;
setRow = [];
}
cur = cell;
count++;
if (map) {
if ( map === 'vert'){
setRow.push([ckey,key]);
} else if ( _.isArray(map)) {
setRow.push([map[key][ckey]]);
}
} else {
setRow.push([key,ckey]);
}
if (count >= toWin) winner = winner.concat(setRow);
});
});
if ( winner.length > 0 ) winners = winners.concat(winner);
return winners;
},
playerWin = function (winners) {
$('.gameboard button')
.addClass('disabled');
_.each(winners, function(w){
$('.gameboard button[data-pos="' + w.join() + '"]')
.addClass('win');
});
$(target).append(
$('<div>')
.addClass('winner')
.append(
$('<h1>')
.html('Congratulations, player ' + symbols[current] + '!')
)
.append(
$('<button>')
.addClass('replay')
.html('Play Again?')
)
);
$('.replay').click ( function(){
setup();
});
};
setup();
}
game();
I want to remove the columns which have total = 0.
So I've tried in different ways.
First, I assigned ID to all columns, for example; every <td> is of column will have their ID eg: First columns <td ID = 'col_1'> , second column all <td ID = 'col_2'> etc. And then in when footer callback I've tried to remove if this column total is ZERO then this $("col_"+i).remove(); this code removed table headers only so I've tried again with $("col_"+i).empty() but again it's just empty. <th> only
Then I've tried to hide the columns by creating dynamic but I don't get any values.
"footerCallback": function ( row, data, start, end, display )
{
var api = this.api(), data;
var intVal = function ( i ) { return typeof i === 'string' ? i.replace(/[\$,]/g, '')*1 : typeof i === 'number' ? i : 0;};
var col_gonna_invis = '[';
for(i=1;i<length_of_coloumns;i++)
{
total_salary = api.column( i ).data().reduce( function (a, b) {return intVal(a) + intVal(b);},0 );
$('#total_cont_'+i).html(total_salary);
if(total_salary == 0)
{
col_gonna_invis += '{"targets": [ '+i+' ], "visible": false, "searchable": false },';
}
}
col_gonna_invis += ']';alert(col_gonna_invis);
},
"aoColumnDefs": col_gonna_invis;
Please someone help me fix this issue or please someone tell me how to hide or remove columns which footer total is 0.
Thank you in advance.
I will suggest you use the visible() API method along with the sum() plugin :
Enhance the API with a column().sum() method :
jQuery.fn.dataTable.Api.register( 'sum()', function ( ) {
return this.flatten().reduce( function ( a, b ) {
if ( typeof a === 'string' ) {
a = a.replace(/[^\d.-]/g, '') * 1;
}
if ( typeof b === 'string' ) {
b = b.replace(/[^\d.-]/g, '') * 1;
}
return a + b;
}, 0 );
} );
now, in initComplete() you can very easy hide columns which have a total or sum() of 0 :
var table = $('#example').dataTable({
//...
initComplete : function() {
var api = this.api(),
colCount = api.row(0).data().length;
for (var i=0; i<colCount; i++) {
if (api.column(i).data().sum() == 0) {
api.column(i).visible(false);
}
}
}
});
demo -> http://jsfiddle.net/qer7e5os/
I am trying to build an array of objects to save a menu's state in a cookie but it is not updating because it is not finding the proper index containing the object I need to update visibility for. What am I doing wrong and/or can you point me in the right direction?
$(document).ready(function() {
var menu = new Array();
$("#vertical-nav .head").click(function() {
var c = $(this).next().attr('name');
$(this).next().slideToggle("slow", function() {
if ( $(this).next().is(':visible') ) {
menu.push({
name: c,
visible: true,
});
} else {
var index = $.inArray(c, menu);
console.log("INDEX: " + index);
menu[index] = { name: c, visible:false };
}
});
console.log(menu);
});
});
This is what I ended up with:
$(document).ready(function() {
var menu = new Array();
if ( $.cookie('menu') ) {
var items = JSON.parse($.cookie('menu'));
for ( var item in items ) {
if ( items[item].visible ) {
$("div[name='" + items[item].name + "']").show();
}
menu.push(items[item]);
}
}
$("#vertical-nav .head").click(function() {
var c = $(this).next().attr('name');
if ( $(this).next().is(":visible") ) {
hide(this, c);
} else {
show(this, c);
}
});
function show(obj, c) {
$(obj).next().slideDown('slow', function() {
var elementExists = false;
if ( menu.length > 0 ) {
for ( var item in menu ) {
if ( menu[item].name == c ) {
elementExists = true;
menu[item].visible = true;
}
}
}
if ( !elementExists ) {
menu.push({
name: c,
visible: true,
});
}
$.cookie('menu', JSON.stringify(menu));
});
}
function hide(obj, c) {
$(obj).next().slideUp('slow', function() {
if ( menu.length > 0 ) {
for ( var item in menu ) {
if ( menu[item].name == c ) {
menu[item].visible = false;
}
}
}
$.cookie('menu', JSON.stringify(menu));
});
}
});
You could easily do this:
if (menu.length > 0) {
for (var item in menu) {
if (menu[item].name=="some text") alert("Found it!");
}
}
What I would consider is instead of [{name: "item"; visible: true}] is switch to a key/value pair. You can then use {"item": true}, but this is limited to only storing the visible value and no extra info can be stored in the key. To solve this, try {"item": {visible: true}}. Here's an example of detection in JS using this approach:
if (menu.length > 0) {
for (var item in menu) {
var strVisible="hidden";
if (menu[item].visible==true) strVisible="visible";
alert(item+" is "+strVisible);
}
}
I am currently working with example 5: http://mleibman.github.io/SlickGrid/examples/example5-collapsing.html so that I can implement collapsing in my own slickgrid. However I am having trouble doing this and was wondering if there is some tutorial I can look at or if someone has done this and can give me a few pointers. The following is some test code that I have been working with to get this to work, without a lot of luck
<script>
var grid;
var data = [];
//this does the indenting, and adds the expanding and collapsing bit - has to go before creating colums for the grid
var TaskNameFormatter = function (row, cell, value, columnDef, dataContext) {
value = value.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
var spacer = "<span style='display:inline-block;height:1px;width:" + (15 * dataContext["indent"]) + "px'></span>";
var idx = dataView.getIdxById(dataContext.id);
if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) {
if (dataContext._collapsed) {
return spacer + " <span class='toggle expand'></span> " + value;
} else {
return spacer + " <span class='toggle collapse'></span> " + value;
}
} else {
return spacer + " <span class='toggle'></span> " + value;
}
};
var columns = [
{id: "title", name: "title", field: "title", width: 150, formatter: TaskNameFormatter},
{id: "duration", name: "Duration", field: "duration"},
{id: "start", name: "Start", field: "start"},
{id: "finish", name: "Finish", field: "finish"}
];
var options = {
enableColumnReorder: false
};
var searchString = "";
function myFilter(item) {
if (searchString != "" && item["title"].indexOf(searchString) == -1) {
return false;
}
if (item.parent != null) {
var parent = data[item.parent];
while (parent) {
if (parent._collapsed || (searchString != "" && parent["title"].indexOf(searchString) == -1)) {
return false;
}
parent = data[parent.parent];
}
}
return true;
}
$(function () {
var indent = 0;
var parents = [];
for (var i = 0; i < 3; i++) {
var d = (data[i] = {});
var parent;
d["id"] = "id_" + i;
if (i===0){
d["title"] = "1st Schedule";
}else if(i===1){
d["title"] = "1st Schedule Late";
}else {
d["title"] = "1st Schedule Early Leave";
}
if (i===0){
parent =null;
}
if (i===1){
parent = parents[parents.length - 1];
indent++;
}
if (i===2){
indent++;
parents.push(1);
}
if (parents.length > 0) {
parent = parents[parents.length - 1];
} else {
parent = null;
}
d["indent"] = indent;
d["parent"] = parent;
d["duration"] = "5 days";
d["start"] = "01/01/2013";
d["finish"] = "01/01/2013";
}
/* **************Adding DataView for testing ******************/
dataView = new Slick.Data.DataView();
dataView.setItems(data);
dataView.setFilter(myFilter); //filter is needed to collapse
/* ************** DataView code end ************************* */
grid = new Slick.Grid("#myGrid", dataView, columns, options);
//this toggles the collapse and expand buttons
grid.onClick.subscribe(function (e, args) {
if ($(e.target).hasClass("toggle")) {
var item = dataView.getItem(args.row);
if (item) {
if (!item._collapsed) {
item._collapsed = true;
} else {
item._collapsed = false;
}
dataView.updateItem(item.id, item);
}
e.stopImmediatePropagation();
}
});
//this is needed for collapsing to avoid some bugs of similar names
dataView.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
grid.render();
});
})
</script>
I strongly suggest you to look into the grouping example instead, it's also with collapsing but is made for grouping, which is 90% of what people want and since couple months can also do multi-column grouping. You can see here the SlickGrid Example Grouping then click on these buttons (1)50k rows (2)group by duration then effort-driven (3)collapse all groups...then open first group and you'll see :)
As for your code sample, I replaced the example with your code and it works just as the example is...so?