2D array reading issue? - google-apps-script

I want to create a sort of "stack" and every time I delete an item, the sheet removes the blank cells. I can't use a filter function for this, obviously.
I am having trouble reading the array that is created for this purpose.
My very pseudo-code : I create an empty array, get all the values (including the empty ones), populate my array with all the values except the empty ones, and finally clear the stack and set the values with my array.
Here is my code :
function updateStack() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("main");
var zone = sheet.getRange(1, 1, 1, 10);
//seems that .getValues() returns a 2d array
var values = zone.getValues();
var j = 0;
var data = new Array();
for (var i = 0 ; i < 10 ; i++) {
//the problem seems to be here : I can't access the 2d array. After reading the debugging console about 1000 thousand times
// I discovered the 2 pairs of []
//I've found multiple ways to detect empty cells. Not sure if this is the right one. I've tried the .length = 0 trick, but something
// was wrong, maybe because of the "2dimensionality"
if (values[i] != "") {
data[j] = values[i];
j = j++;
} else {
// do nothing if the cell contains nothing
}
//not sure if I have to use return ! Don't know where to put it exactly too...
return data;
zone.clear();
//length of range must be the same as the array's length
zone = sheet.getRange(1, 1, 1, data.length);
zone.setValues(data);
}
}
There are many comments in my code, I hope you will understand.
A link to my test sheet : http://bit.ly/1JiWutn
Thanks for any help !

Currently, you have a section of code like this:
if (values[i] != "") {
data[j] = values[i];
j = j++;
} else {
You are testing for an empty string:
values[i] != ""
But values[i] is an inner array. Your code is getting only one row, and 10 columns.
var zone = sheet.getRange(1, 1, 1, 10);
So, the array looks like this:
[ [cell one,cell two,cell three,etc,cell ten ] ]
values[i] returns an inner array, not a value.
To get the cell value use:
if (values[0][i] != "") {
You need two indexes, the first index will always be zero. There is only one inner array with all the cell values in it.
Next, use push to add a value to the data array:
data.push(values[0][i]);
Another issue is where you have the return statement. A return statement kills the current function. Anything after the return statement inside of that function will not run. So, you can't have a return statement where you have it, and get the code to write values to the spreadsheet. You can do both. You can both write values to the sheet, and return something, but put the return at the end. The return, returns something to whatever function called this function.
To set values, the values MUST be in a two dimensional array. Your data array is not a 2D array. You must add the data array to yet another array.
var my2Darray = [];
my2Darray.push(data);
zone = sheet.getRange(1, 1, 1, data.length);
zone.setValues(my2Darray);

If you are only testing for blank cells across a whole row then the test you have is almost usable. If you concatenate all the values in that array then the resultant can be compared to "".
// join all the values with nothing between them
// compare the result to empty string
if(values[i].join("") !== "") {
// if at least one cell contained something
data.push(values[i]); // Stackius Popularious
}

Related

Google Apps Script - Problem with simple if statement

I am trying to write a simple 'If statement' that for some reason isn't able to properly find what data is in the cell it's referencing('Data' S26). It should contain either a 0 or a 1, but it doesn't seem to matter if I have a 0 or a 1 in the cell - either from a formula or directly, the following formula is always returning a "2". Completely new to this so probably something easy, but I can't seem to find the answer!
Thanks!
function InsertWinLoss(){
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
var gameSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Games');
var currentRow = gameSheet.getLastRow();
if (dataSheet.getRange('S26') =='1') {
gameSheet.getRange(currentRow,3).setValue('1');
} else if (dataSheet.getRange('S26') = '0') {
gameSheet.getRange(currentRow,3).setValue('0');
} else { gameSheet.getRange(currentRow,3).setValue('2') }
}
Seems like the original post might have been answered in comments, but for what it's worth:
Call getValue() on the Range returned by the conditions in the if statements
The else if condition has a syntax error; the expression uses the assignment operator =, rather than checking for equality ==
As originally written, the setValue() calls are writing 1, 0, or 2 as Strings, rather than Numbers; omit the '' if this isn't intended
Here's my two cents on rewriting the function to be a little more compact! Hope it's useful.
//By convention, functions should start lowercase
function insertWinLoss() {
//Get Sheets
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
var gameSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Games');
//Assign value of your win/loss cell to new variable
var winLossValue = dataSheet.getRange('S26').getValue();
//Set your output range
var currentRow = gameSheet.getLastRow();
var outputRange = gameSheet.getRange(currentRow, 3);
//Now your if/else statements are a little more compact
//and it's easier to change values like the win/loss cell or output column!
if (winLossValue == 1) {
outputRange.setValue('1');
} else if (winLossValue == 0) {
outputRange.setValue('0');
} else {
outputRange.setValue('2')
}
}
Function should give desired result of writing 1, 0, or 2 to output range based on input to win/loss range.

Receiving error The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues [duplicate]

I am getting this error:
"The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues."
in my Google Apps Script when I try to write an array of values to a sheet.
Below is a shortened (simplified) version of code. The actual code runs through about 10,000 records.
The error is generated in the last line, when the setValues is called.
I know I'm missing something super simple here.
function writeArrayToSheet() {
var ss = SpreadsheetApp.openById("Spreadsheet_ID");
var orderSheet = ss.getSheetByName("Sheet_Name");
var vTable = orderSheet.getRange(1,6,5,11).getValues(); //Raw data
var vWriteTable = []; //Data that will be written to sheet
var updateTime = new Date();
var i = 0;
var vSeconds = 0;
while (i < 5 && vTable[i][0] != "") {
//Logic section that calculated the number of seconds between
if (vSeconds == 0) {
vWriteTable.push("");
} else {
if (vTable[i][6] < certain logic) {
vWriteTable.push("Yes");
} else {
vWriteTable.push("");
}
}
i = i + 1;
} // End while
orderSheet.getRange(1,20,vWriteTable.length,1).setValues(vWriteTable);
} //End Function
This is what vWriteTable looks like when debugging:
setValues accepts(and getValues() returns):
1 argument of type:
Object[][] a two dimensional array of objects
It does NOT accept a 1 dimensional array. A range is always two dimensional, regardless of the range height or width or both.
If A1:A2 is the range, then corresponding values array would be like:
[[1],[3]]
Similarly, A1:B1 would be
[[1,2]]
A1:B2 would be
[[1,2],[3,4]]
Notice how the two dimension provides direction and that it is always a 2D array, even if the height or width of the range is just 1.
Solution:
Push a 1D array to make the output array 2D.
Snippet:
vWriteTable.push(/*Added []*/["Yes"]);
More information:
For a more detailed explanation of arrays in google sheets, checkout my answer here.
getValues returns object[rows][columns]
1 row object [[1...n]] - this maybe confusing b/c it may not look like 2D but it is 2D object[1 row][n columns]
2 row object [[1...n],[1...n]] - object[2 rows][n columns]
etc [[1...n],...,[1...n]] - etc
If you need to setValues to rectangular range with n rows and m columns, create EXACTLY n by m array 1st, then fill it with your values and send it to the MATCHING range in your spreadsheet. Here is some of my code that might help.
Getting values from 1st m cells in n-th row and pushing them in simple 1D array:
var arr = [];
var dataRow = sheet.getRange(n,1,1,m).getValues();
for (var j=0; j<dataRow.length; j++){
arr.push(dataRow[0][j]);
}
to replace them with new values from 1D array arr:
//create 2D array with just 1 row and m columns
var dataRow = new Array(1);
dataRow[0] = new Array(arr.length);
//fill it with values
for(var j=0;j<arr.length;j++){
dataRow[0][j]=arr[j];
}
//send it to the spreadsheet
sheet.getRange(n,1,1,arr.length).setValues(dataRow);

Apps Script, difficulty on trigger action when 2 values match [duplicate]

I am getting this error:
"The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues."
in my Google Apps Script when I try to write an array of values to a sheet.
Below is a shortened (simplified) version of code. The actual code runs through about 10,000 records.
The error is generated in the last line, when the setValues is called.
I know I'm missing something super simple here.
function writeArrayToSheet() {
var ss = SpreadsheetApp.openById("Spreadsheet_ID");
var orderSheet = ss.getSheetByName("Sheet_Name");
var vTable = orderSheet.getRange(1,6,5,11).getValues(); //Raw data
var vWriteTable = []; //Data that will be written to sheet
var updateTime = new Date();
var i = 0;
var vSeconds = 0;
while (i < 5 && vTable[i][0] != "") {
//Logic section that calculated the number of seconds between
if (vSeconds == 0) {
vWriteTable.push("");
} else {
if (vTable[i][6] < certain logic) {
vWriteTable.push("Yes");
} else {
vWriteTable.push("");
}
}
i = i + 1;
} // End while
orderSheet.getRange(1,20,vWriteTable.length,1).setValues(vWriteTable);
} //End Function
This is what vWriteTable looks like when debugging:
setValues accepts(and getValues() returns):
1 argument of type:
Object[][] a two dimensional array of objects
It does NOT accept a 1 dimensional array. A range is always two dimensional, regardless of the range height or width or both.
If A1:A2 is the range, then corresponding values array would be like:
[[1],[3]]
Similarly, A1:B1 would be
[[1,2]]
A1:B2 would be
[[1,2],[3,4]]
Notice how the two dimension provides direction and that it is always a 2D array, even if the height or width of the range is just 1.
Solution:
Push a 1D array to make the output array 2D.
Snippet:
vWriteTable.push(/*Added []*/["Yes"]);
More information:
For a more detailed explanation of arrays in google sheets, checkout my answer here.
getValues returns object[rows][columns]
1 row object [[1...n]] - this maybe confusing b/c it may not look like 2D but it is 2D object[1 row][n columns]
2 row object [[1...n],[1...n]] - object[2 rows][n columns]
etc [[1...n],...,[1...n]] - etc
If you need to setValues to rectangular range with n rows and m columns, create EXACTLY n by m array 1st, then fill it with your values and send it to the MATCHING range in your spreadsheet. Here is some of my code that might help.
Getting values from 1st m cells in n-th row and pushing them in simple 1D array:
var arr = [];
var dataRow = sheet.getRange(n,1,1,m).getValues();
for (var j=0; j<dataRow.length; j++){
arr.push(dataRow[0][j]);
}
to replace them with new values from 1D array arr:
//create 2D array with just 1 row and m columns
var dataRow = new Array(1);
dataRow[0] = new Array(arr.length);
//fill it with values
for(var j=0;j<arr.length;j++){
dataRow[0][j]=arr[j];
}
//send it to the spreadsheet
sheet.getRange(n,1,1,arr.length).setValues(dataRow);

Increment column of cells by 1 if a condition is met

I am trying to make a macro that will change columns C:5 to C:bottom of the sheet.
These cells should be incremented by 1 if the cell to the left (B:5 to end) is equal to the string 'TRUE'.
Here is what I have so far but the condition is not being met. I also don't know the best way to apply this to the whole column. Should I do a for loop?
Here is what I have so far
function increment() {
var spreadsheet = SpreadsheetApp.getActive();
var reocurring = spreadsheet.getRange("B5").getValue().toString();
if (reocurring == 'TRUE')
{
var value = spreadsheet.getRange('C5').getValue();
spreadsheet.getRange('C5').setValue(value + 1);
}
};
Issue:
Your if statement condition isn't being met. This is because getValue() returns values with their associated type. Your "TRUE" value is being recognized as a boolean, not string like your if statement is looking for. You are using toString() but this changes the value to 'true' rather than 'TRUE', so your if statement condition still is not met.
Solution:
There are a few ways around this:
Change your if statement to look for a boolean:
if (reocurring === true)
Force the value to string and upper case to make sure your condition is met:
var reocurring = spreadsheet.getRange("B5").getValue().toString().toUpperCase();
Use getDisplayValue() to return a string from the beginning:
var reocurring = spreadsheet.getRange("B5").getDisplayValue();
Looping through the sheet:
Since this could be quite a long-running script if you're running it for a large data set, it's best to work with arrays here. I've tried to comment the code so it's a little easier to understand but if you have any questions please let me know.
function increment() {
var spreadsheet = SpreadsheetApp.getActiveSheet();
//get array values in columns B and C starting from row 5
var array = spreadsheet.getRange(5, 2, spreadsheet.getLastRow(), 2).getValues();
//loop through array
for (i = 0; i < array.length; i++) {
if (array[i][0] === true) {
//increment value in row C by 1
array[i][1] = array[i][1] + 1;
}
}
//set new incremented values
spreadsheet.getRange(5, 2, array.length, array[0].length).setValues(array);
}
References:
getValues()
setValues()
JavaScript Arrays
JavaScript Data Types

How can I shift rows by one when they match a substring?

Could someone please help me solve this?
Attached is the example sheet of what I am trying to do:
https://docs.google.com/spreadsheets/d/12w4rGArGi1I1wlpm5yJJtT5AAlMM4LcZC31_DpP6jZQ/edit?usp=sharing
I am trying to shift the rows that contain "PO" in my data selection. (see shift function)
It should change from this:
to this:
I have written a script but it isn't working and I am not getting an error message.
I have a feeling it is because I am trying to "+1" my array to offset my values. Please help!
Here is my current script:
function shift() {
try{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var as = ss.getActiveSheet();
var ar = as.getActiveRange();
var vals = ar.getValues(); // get values from the active (selected) range...... intended use is to draw a selection where the PO BOX substring is in the leftmost column
// SpreadsheetApp.getUi().alert(vals); //for checking values
var r; // variable for rows
for (r = 0; r < vals.length; r++){ // for each row, up to the last row (iterate over all rows from top to bottom)
if(vals[r][0].indexOf("PO") != -1){ // if first column in each row contains "PO"
SpreadsheetApp.getUi().alert("found a PO BOX"); // make annoucement
var c; // variable for columns
var cols = []; // array for column data (for debugging)
for (c = 0; c < vals[r].length; c++){ // for each columns in row (iterating over each cell in row from left to right)
cols[c] = vals[r][c]; // add the current cell value to an array
vals[r][c+1] = vals[r][c]; // take the value from the current cell and assign it to the next cell (+1 to the column)
}
SpreadsheetApp.getUi().alert(cols); // show me the data that cas changed
}
}
ar.setValues(vals); // set new values to active range
}
catch(err){
SpreadsheetApp.getUi().alert(err);
}
}
Expectations:
get the data range (my intended testing range is B:1 to D:12)
iterate through each row, and on each row, iterate through each
cell(column)
If the very first cell in the current row(vals[r][0]) contains the
substring "PO" , then I want to change the values of that row such
that they all shift over by one column, and leave the very first
cell as a blank string
I change the values by replacing the current values with the same
values BUT +1 to the columns row(vals[r][c]) = row(vals[r][c+1])
Realit(EDIT)y:
Exceeded memory limit error... possible infinite loop happening... i THINK it might be because I am adding a column that does not exist in the data range, but how would I get around this problem?
SOLVED!!!!
After much trial and error, I cane up with an answer (posted int he answers)
feels great to solve my own problem for the first time ever!
my problem was exactly what i expected. I was trying to assign values outside of the range.
The way that I solved this was to assign all of my values to an array(I also shifted them over in the process), and then loop through the range(the range is just a two dimensional array), assigning all of the shifted values to the range columns.
function shift() {
try{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var as = ss.getActiveSheet();
var ar = as.getActiveRange();
var vals = ar.getValues();
var r; //variable for rows
for (r = 0; r < vals.length; r++){ // for each row, up to the last row (iterate over all rows from top to bottom)
if((vals[r][0].indexOf("PO") != -1)||(vals[r][0].indexOf("P0") != -1)){ // if first column in each row contains "PO"
var c; // variable for columns
var cols = []; // array to store all data temporarily (will be uses to set new values later)
for (c = 0; c < vals[r].length; c++){ // then iterate over each column(cell) in the row
if(c == 0){ // if it is the first row,
cols[c+1] = vals[r][c]; // assign second index of the array with the PO value (to simulate a shift)
cols[c] = ""; // assign the first index of the array a blank string
}
else{ // if it is not the first row
cols[c+1] = vals[r][c]; // assign each additional column value to the next index (+1) of the array
}
}
for (c = 0; c < vals[r].length; c++){ // once the array is finished, loop through the columns again foreach row
vals[r][c] = cols[c]; // this time, assigning the new values to the corresponding array indices
}
}
}
ar.setValues(vals); // now, set the values that you reassinged to the array
}
catch(err){
SpreadsheetApp.getUi().alert(err);
}
}