If column contains text add a checkbox in column apps script google sheets - google-apps-script

I am copying data from a spreadsheet titled after the specific month and placing it in my main spreadsheet. I have successfully copied the data into range K80:K94 on my Daily Hub sheet.
In range K80:K94 I now want to add a checkbox in column M if there is a value in column K. For example if there is a value in K80 and K81 there would be a checkbox in M80 and M81. I feel like this should be fairly straightforward, however I have tried a few different options including using IsBlank() and nothing seems to be working.
function dailyhubhabits() {
var montha = new Array(12);
montha[0] = "JANUARY";
montha[1] = "FEBRUARY";
montha[2] = "MARCH";
montha[3] = "APRIL";
montha[4] = "MAY";
montha[5] = "JUNE";
montha[6] = "JULY";
montha[7] = "AUGUST";
montha[8] = "SEPTEMBER";
montha[9] = "OCTOBER";
montha[10] = "NOVEMBER";
montha[11] = "DECEMBER";
var dailyhabitshubmonth = new Date();
var getdhmonth = montha[dailyhabitshubmonth.getMonth()];
Logger.log(getdhmonth);
var mhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(getdhmonth);
var monthhabitsogdata = mhs.getRange("C56:E70");
var gethabits = monthhabitsogdata.getValues();
Logger.log(gethabits);
var dhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DAILY HUB");
var habitsarea = dhs.getRange("K80:K94");
monthhabitsogdata.copyTo(habitsarea);
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
}

You want to insert a checkbox on Column M when there is a value in the same row of column K.
There are two problems with this part of your script:
evaluating whether the cell has a value
defining the target range for the checkbox
Does the cell have a value?
length returns the number of records in an array, but it is not a good method for determining whether a cell contains a value. This is a popular topic; you might care to read Google Spreadheets Scripts: check if cell is empty for several methods.
a better approach is !== ""
Defining the target cell
dhs.getRange(i+1,14).insertCheckboxes(); - there are two problems here
Column M is 13
i starts at zero, so the first range value would be .getRange(1,14) = Cell N1.
so you need a variable that defines the startRow, such as:
var startRow = 80
REPLACE
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
WITH
var startRow = 80
var endRow = 94
var datavalues = dhs.getRange("K"+startRow+":K"+endRow).getValues()
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0] !=="") {
dhs.getRange(i+startRow,13).insertCheckboxes()
}
}

SUGGESTION
In my understanding, here's your goal:
Check values in K80:K94
Insert a checkbox on a row in M that is adjacent to a row that isn't empty in the K80:K94 range.
Perhaps you could try this sample script to replace your current line on the section in inserting the check-boxes:
/** SUGGESTION
* 1. Iterate through the values in range K80:K94 & identify which aren't empty.
* 2. Get each non-empty values' row numbers.
* 3. To reduce runtime execution in the loop, if there are consecutive non-empty values, set them as a range (e.g. M80:M81). Otherwise a single value will be set as a single range (e.g. M83);
* 4. Iterate through these ranges & insert the checkboxes.
*/
var range = SpreadsheetApp.getActive().getRange('K80:K94');
var temp_values = range.getValues().map((x, i) => x != '' ? [x, (range.getLastRow() - (range.getNumRows() - i) + 1)].flat() : '*');
var ranges = temp_values.join().split('*').map(y => (y.replace(/[a-zA-Z,]+/g, '-')).split('-').filter(x => x != ''));
ranges.map(z => [...new Set([z[0], z[z.length - 1]])]).forEach(
row => row.length > 1 ? SpreadsheetApp.getActive().getRange(`M${row[0]}:M${row[1]}`).insertCheckboxes() :
SpreadsheetApp.getActive().getRange(`M${row[0]}`).insertCheckboxes()
);
/** End */
This sample script runs faster vs your current implementation as it shortens the data to be processed in the loop
Demo
Sample sheet
After running the script

Related

Exception: The number of columns in the data does not match the number of columns in the range. The data has 0 but the range has 1

I am very new to javascript and have searched around a ton for this and can't seem to find the issue with my code. I am attempting to simply write a code that will copy the values in a column from a pivot table sheet in Google Sheet and then paste the values in another sheet. However, before pasting the values, I want each individual value to be duplicated 12 times (for 12 months). So, assuming I have 10 unique values (A, B, C, D, E, F, G, H, I, J) that I am copying, I want to return value A 12 times in a row, then value B 12 times in a row, etc.
I run getValues, which seems to put the values in a 2 dimensional array. I've then taken this temp_array that I had created and used a for loop to duplicate each value 12 times in a new array.
However, when I setValues, I am pasting the values in my spreadsheet correctly, but I get this error message regardless (The number of columns in the data does not match the number of columns in the range. The data has 0 but the range has 1.), any ideas why?
Here is a small example of what my input could look like (1st image) and what I would want the output to look like (2nd image)
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActive();
//empty array
var array_dept_temp = [];
// returns cell position (ex: C5) of the last row of the pivot table 1 sheet that has content in column 1
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getRange("A:A").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRowIndex();
//subtracting 1 from last row because we are excluding the headers. This gives us our row_length
var row_length = last_row - 1
var array_dept = [[]]
array_dept = new Array(row_length*12)
//new Array(row_length*12);
// Get value in pivot table 1 from range of row 2 (dept name, but exclude the header), column 1, all the way to last row
// Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
array_dept_temp = spreadsheet.getSheetByName("Pivot Table 1").getRange(2,1, last_row).getValues();
for (var i = 1; i < row_length; i++ )
{
//get value and then paste it in a destination
array_dept.fill(array_dept_temp[i-1], (-12 + (12*i)) , 12*i);
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2,3,row_length*12);
destination_dept.setValues(array_dept);
}
Suggestion / Alternate solution:
Try:
function test() {
  var spreadsheet = SpreadsheetApp.getActive();
  var sheet = spreadsheet.getSheetByName("Pivot Table 1");
  var array_dept_temp = sheet.getRange(2,1, sheet.getLastRow()-1).getValues();
  var array_dept = [];
  for (var i = 0; i < array_dept_temp.length; i++) {
    array_dept = [...array_dept, ...Array.apply(null, Array(12)).map(function(){return array_dept_temp[i]})]
  }
  var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2,3,array_dept.length);
  destination_dept.setValues(array_dept);
}
Result:
Another way without using fill or from.
Also some modification, you can just use .getLastRow() function to get the last row, however take not that if there is data below it will count all the rows including the blank until the row that has data. And you may also use .length on your data to setValue.
From your showing sample input and output situations, how about the following modified script?
Modified script:
function test2_sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("Pivot Table 1");
var dstSheet = ss.getSheetByName("Sheet5");
var srcValues = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).getValues();
var dstValues = srcValues.flatMap(a => Array(12).fill(a));
dstSheet.getRange(2, 3, dstValues.length).setValues(dstValues);
}
When this script is run using your sample input sheet, I think that your expected output values are obtained.
Now, I thought that var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e])); can be modified to var dstValues = srcValues.flatMap(a => Array(12).fill(a));. This is simpler.
From your reply of Are you able to explain what this does? var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e]));, in this script, var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e])); can be also modified as follows. I thought that this might also help to understand it.
function test2_sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("Pivot Table 1");
var dstSheet = ss.getSheetByName("Sheet5");
var srcValues = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).getValues();
var dstValues = [];
for (var i = 0; i < srcValues.length; i++) {
dstValues = dstValues.concat(Array(12).fill(srcValues[i]));
}
dstSheet.getRange(2, 3, dstValues.length).setValues(dstValues);
}
Note:
As additional information, when your showing script is modified, how about the following modification? In your script, I thought that it is required to add the values to array_dept in the loop. And, it is required to flatten the elements in the array.
function test2() {
var spreadsheet = SpreadsheetApp.getActive();
var array_dept_temp = [];
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getRange("A:A").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRowIndex();
var row_length = last_row - 1
var array_dept = []
array_dept_temp = spreadsheet.getSheetByName("Pivot Table 1").getRange(2, 1, last_row).getValues();
for (var i = 0; i < row_length; i++) {
array_dept = [...array_dept, ...Array(12).fill(array_dept_temp[i])];
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2, 3, array_dept.length);
destination_dept.setValues(array_dept);
}
Reference:
flatMap()

How do you create a Uniq ID in Google Sheets using apps script?

Im looking at creating a simple Uniq ID after column A has information entered and need for the ID to show on column I. I want to call it Trip Number and display Driver-John0001 and so forth using google sheets script. Sorry I'm new to this so I don't know the lingo
The current code I had found works but now I need it a bit different. This is what my results show Driver:1611710811706
I would like to pull Driver-John0001. Where the name John is generated by the column labeled Driver or column D
How do I change it to add the value on column D + 4 digit numbers that don't repeat?
function TripID () {
{ var sheetVals = [
["DriverLog","Driver:","",9,1]
];}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Loop through every sheet value
for(var i = 0; i < sheetVals.length; i++){
var sheetID = sheetVals[i][0],
frontStr = sheetVals[i][1],
backStr = sheetVals[i][2],
IDcol = sheetVals[i][3],
editCol = sheetVals[i][4];
var offset = IDcol - editCol;
if(sheet.getName() === sheetID){
var date = new Date().getTime();
var newID = frontStr+date+backStr;
//Check the location of the active cell
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() === editCol) {
//Update the ID Column
var cellToChange = selectedCell.offset(0,offset);
if(cellToChange.isBlank()){
cellToChange.setValue(newID);
};
};
};
};
};
Assuming that you will never need more than 9999 IDs, then you could modify your code with the following steps to accomplish this. Note that this solution will allow you to create a (nearly) unlimited number of ID numbers, but after 9999, the length of the id numbers will vary.
Use the PropertiesService to initialize a counter for creating IDs
function createIdProperty() {
PropertiesService.getScriptProperties().setProperty('idCounter', '0');
}
// run this once to create the idCounter property
createIdProperty();
// To test that your property was created you can run this
Logger.log(PropertiesService.getScriptProperties().getProperty('idCounter')); // logs '0'
Create a function to get the next counterId number from Script Properties as a string with 0s replacing empty digits ( 1 => '0001', 2 => '0002', 3 => '0003')
function getUniqueIdNumber() {
var currentIdNum = +PropertiesService.getScriptProperties().getProperty('idCounter'); //get counter as a number
currentIdNum++; // increment counter by 1
currentIdNum+=''; // convert counter back to a string
PropertiesService.getScriptProperties().setProperty('idCounter', currentIdNum); // store the new counter in properies
while (currentIdNum.length < 4) {
currentIdNum = '0'+currentIdNum;
}
return currentIdNum;
}
modify your original code to use getUniqueIdNumber instead of date for creating your id
Change
var newID = frontStr+date+backStr;
To
var idNumber = getUniqueIdNumber();
var newID = frontStr+idNumber;
Note that you can replace frontStr and backStr with any variables you want.

Get Collection Of Cells With A Certain Value Google Sheets

I have a button that I want to click, which will scroll me to a certain position. I've done this in order to get me to row 100:
function ScrollMe(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var sheet = file.getActiveSheet();
var row = 100;
file.setActiveCell(sheet.getRange(row,1));
}
What I want to do if a find a list of all cells that are in column 'B' that contain (REGEX=>"Version: [0-9]?[0-9]?[0-9][.]?[0-9]?[0-9]? [a-zA-Z]+"), and then go to the last value that this is like. So basically, go to the last cell in column 'B' that starts with "Version: " and then has a single, double, or triple-digit number, a decimal point, and then two numbers after, and then any amounts of letter text after the fact. I want it to look like this:
function ScrollMe(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var sheet = file.getActiveSheet();
//C# lambda
var row = FindAll(a=> a.COLUMN == 'B' && a.VALUE.RegexMatch("Version: [0-9]?[0-9]?[0-9][.]?[0-9]?[0-9]? [a-zA-Z]+"));
file.setActiveCell(sheet.getRange(row,1));
}
I assume that you expect the script to find the last cell in the column B that match your regex. If that is the case, you can use this code:
function ScrollMe() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getRange("B:B").getValues();
var regex = new RegExp(
'Version: [0-9]?[0-9]?[0-9][.]?[0-9]?[0-9]? [a-zA-Z]+');
for (var i = 0; i < data.length; i++) {
if (regex.test(data[i][0])) {
var lastMatch = i;
}
}
sheet.setActiveRange(sheet.getRange(lastMatch + 1, 2));
}
The previous code used your approach. It will first read the full column B, and after that will iterate to find the last cell that match the regex; and when it finds the cell, it will select it. Please, ask me if you have any doubts about the function.

Looping through rows, get values, and add to total on another sheet

So I'm stumped on this in Google Sheets.
Sheet 'Price Calculator' Qty has a of items bought and sold in Column A, separated into 2 named ranges TRADE_QTY and BUY_QTY.
An identical List appears in sheet 'Master Tally', with qtys from previous trades, also in column A.
Have been flipping through multiple windows of examples of code and none seem to be able to provide anything that works.
function TEST() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
//Gets number of rows of each range
var Rows1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getNumRows()
var Rows2 = ss.getRange('\'PRICE CALCULATOR\'!BUY_QTY').getNumRows()
//Gets Starting rows of each range
var Row1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getRow()
var Row2 = ss.getRange('\'PRICE CALCULATOR\'!BUY_QTY').getRow()
for (r=Row1; r<Rows1; r++) {
ss.setActiveSheet(ss.getSheetByName('PRICE CALCULATOR'), true);
var ADD = ss.getRange(r,1).getValue()
if (cell.isBlank()) {
next r
}
else {
ss.setActiveSheet(ss.getSheetByName('Master Tally'), true);
var EXIST = ss.getRange(r,1).getValue()
var TOT = ADD+EXIST
ss.getRange(r,1).setValue(TOT)
}
}
}
Basically i'm try to develop a macro/script that adds the new trade qtys in sheet 'Price Calculator' to the existing qtys in 'Master Tally'
I"m stumped as it keeps throwing me 'Cannot find method getRange(number,number)' and now i'm out of my depth!
Link to the document;
https://docs.google.com/spreadsheets/d/1gIjCqv5KT41wYuJS1Hs1X8yPPUTPY_kGoTuilzxLkSo/edit?usp=sharing
This code suffers from a basic flaw: confusion between Row and Column numbers of an array (which start at 0-zero) with those derived from script commands such as getValue (which start at 1-one).
For example:
for (r=Row1; r<Rows1; r++) {
In this case, the value of Row1 was determined by getRow, so it returns the actual row number. But the loop values will generate the row and column number for an array starting at zero; so this line should read for (r=0; r<Rows1; r++) {
var EXIST = ss.getRange(r,1).getValue()
The purpose of this line is return the "existing qtys in 'Master Tally'", and the range will look in Column A for the value. However the values are actually in column B. So this line will never return an accurate value for "existing qtys".
There are some other things to note:
The existing code makes two getValue calls in every loop; these are time-expensive. The answer improves performance by getting the respective range values just once before the loop.
The update of the quantity sold (setValue(TOT)) is inside the loop. Again this is a time-expensive command. The answer updates the array values within the loop, and then updates the sheet once-only after the loop.
BUY Qty values are irrelevant
function so56017521() {
var ss = SpreadsheetApp.getActive();
//Gets number of rows of each range
var Rows1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getNumRows()
//Logger.log("DEBUG: Number of Rows: Trade Qty="+Rows1);
//Gets Starting rows of each range
var Row1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getRow()
//Logger.log("DEBUG: Start Row: Trade Qty="+Row1);
// setup sheets
var calcsheet = "PRICE CALCULATOR";
var mastersheet = "Master Tally";
var calc = ss.getSheetByName(calcsheet);
var master = ss.getSheetByName(mastersheet);
var masterrows = master.getLastRow();
//Logger.log("DEBUG: Master Last Row = "+masterrows);
// get data for each sheet
var calcrange = calc.getRange(Row1, 1, Rows1);
var calcdata = calcrange.getValues();
var masterrange = master.getRange(3, 2, masterrows - 2);
var masterdata = masterrange.getValues();
//Logger.log("DEBUG: Calc data range = "+calcrange.getA1Notation()+", Master Data Range"+masterrange.getA1Notation());
for (r = 0; r < Rows1; r++) {
Logger.log("r=" + r);
var ADD = calcdata[r][0]; //Trade qty
//Logger.log("DEBUG: r="+r+", ADD value = "+ADD+", ADD.length = "+ADD.toString().length);
if (ADD.toString().length != 0) { // if Trade qty has value
// keep going
//Logger.log("DEBUG: keep going");
var EXIST = masterdata[r][0]; // existing quantity qty sold
Logger.log("DEBUG: r=" + r + ", EXIST = " + EXIST);
var TOT = ADD + EXIST; // sum of trade-in qty plus existing qty
Logger.log("DEBUG: ADD+EXIST = " + TOT);
// update masterdata array
masterdata[r][0] = TOT;
} else {
// nothing to see here
//Logger.log("DEBUG: next r please");
}
}
//update the spreadsheet with the adjusted array values
masterrange.setValues(masterdata);
}

Automatically move data from one sheet to another in google docs

i have a spreadsheet that i keep track of tasks i need to do, once complete i enter a date in the last column. What i want is for that completed task to be moved to sheet 2.
At present i have sheet 1 named SUD_schedule and i want the completed row of data to be moved to sheet 2 named SUD_archive. I've looked through the forum posts already and i've tried a variation of scripts but so far no luck. The closest i have come is this script:
function onEdit() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();//Original sheet
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];//target sheet
// to act on only one sheet, check the sheet name here:
//If it si not first sheet, it will do nothing
if (sheet1.getSheetName() != "SUD_schedule") {
return;
}
//Get Row and column index of active cell.
var rowIndex = sheet1.getActiveRange().getRowIndex();
var colIndex = sheet1.getActiveRange().getColumnIndex();
//If the selected column is 10th and it is not a header row
if (colIndex == 16 && rowIndex > 1) {
//Get the data from the current row
var data = sheet1.getRange(rowIndex,1,1,9).getValues();
var lastRow2;
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
//Copy the data to the lastRow+1th row in target sheet
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
}
}
Column P (16) is the task complete date, row 1 is frozen and contains column headers.
Can anybody help show where i'm going wrong.
Kind regards
Den
Your code is not generic and you are more complicating your objective. Below will work out your need.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('SUD_schedule');
var sheet2 = ss.getSheetByName('SUD_archive');
var dateColumn = "16";
var array = []
var range = sheet1.getRange(1, 1, sheet1.getLastRow(), dateColumn);
for (var i = 2; i <= sheet1.getLastRow(); i++) //i iterates from 2 as you say R1 is header
{
if(isValidDate(range.getCell(i, dateColumn).getValue()) == true) //checking if any values on column16 is valid date
{
data = sheet1.getRange(i, 1, 1, dateColumn).getValues(); //Getting the range values of particular row where C16 is date
for (var j = 0; j < dateColumn; j++) //Adding the row in array
{
array.push(data[0][j]);
}
}
if(array.length > 0)
{
sheet2.appendRow(array); //Appending the row in sheet2
array = [];
sheet1.deleteRow(i); //deleting the row in sheet as you said you want to move, if you copy remove this and next line
i=i-1; //managing i value after deleting a row.
}
}
}
//Below function return true if the given String is date, else false
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
I am not sure that the syntax you have as used below is entirely correct.
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
What I know will work for certain is if you omit the variable lastRow2 all together and use this instead.
sheet2.getRange(getLastRow+1,1,1,data[0].length).setValues(data);
To complement Joachin's answer, here is how you can adapt that code if you don't have the date in the last row. In the below shown part of the code replace Lastcolumnumber with your last column.
//Getting the range values of particular row where C16 is date
data = sheet1.getRange(i, 1, 1, LASTCOLUMNNUMBER).getValues();
//Adding the row in array
for (var j = 0; j < LASTCOLUMNNUMBER; j++)