Copy/paste values using google app script - google-apps-script

Here is the sheet with an example of the data that I'm using
https://docs.google.com/spreadsheets/d/1_k3xclEgdREfMMks7H2-uk0tzxXqCaoeVW_G8ase-3o/edit?usp=sharing
I only put the formulas on the rows without color, and the information about the schedule comes from other tab.
the colors in green are the ones with dates, where I store inside the numbers, and the blue ones also contain formulas and I wanna skip those columns when pasting the values because is correlated to another worksheet, so I can't paste the values
I've tryied two ways
this first one I receive an erro message: Exception: The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues.
//destination = sheet.getRange(1,colA[j],table.length,1)
//destination.setValues(table); // where we paste the values by column
For this one, it paste on other tab on the first columns
//sheet.getRange(1,colA[j],table.length,1).copyTo(sheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Here is the script that Im using
function PasteValues() {
var ss = SpreadsheetApp.openById("...");
var sheet = ss.getSheetByName("Testsheet");
var rows = sheet.getDataRange().getValues();
var dates = rows[2];
//Logger.log(dates)
var yesterday = new Date(Date.now() - 864e5);
var numbers = [];
for(var i = 2; i < dates.length; i++) {
let columns = i
if (dates[i]!=="" && dates[i] !== null){
numbers.push(columns);
}
if (dates[i]==="") {
continue;
}
if (dates[i].getDate() == yesterday.getDate() && dates[i].getMonth() == yesterday.getMonth() ){
break;
}
}
colA=numbers.slice(-5)
var table = [];
Logger.log(rows.length)
Logger.log(colA)
for(var j=0;j<colA.length;j++)
{
table =[];
for (var i = 0; i < rows.length;i++ )
{
table[i] = rows[i][colA[j]];
}
Logger.log("the number of the column is: "+colA[j]);
Logger.log(table);
// where I paste the data
}
}
This is the example on how my data is to copy/paste it based on the column number

When you retrieve values from the spreadsheet, getValues() already returns them to you in a 2-D array - there is no need to manually transfer them into another array
You can either do:
var table = sheet.getDataRange().getValues();
destination = sheet.getRange(1,statColumn,table.length,table[0].length);
destination.setValues(table);
Or:
sheet.getDataRange.copyTo(sheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
UPDATE
Exception: The parameters (number[]) don't match the method signature
for SpreadsheetApp.Range.setValues.
means that you are trying to assign a row (1-D array) to a range (2-D array).
Also, table.length will retrieve you the number of columns and not rows if table is a row.
This can be easily solved by defining:
table = [table];
Sample snippet:
for(var j=0;j<colA.length;j++)
{
table =[];
for (var i = 0; i < rows.length;i++ )
{
table[i] = rows[i][colA[j]];
}
Logger.log("the number of the column is: "+colA[j]);
table = [table];
Logger.log(table);
// where I paste the data
destination = sheet.getRange(1,colA[j],table.length,1)
destination.setValues([table]); // where we paste the values by column
}
UPDATE
If what you want is to copy paste selected data column by column, you need to create a 2D array table and populate it as following:
for(var i = 2; i < dates.length; i++) {
let columns = i
if (dates[i]!=="" && dates[i] !== null){
numbers.push(columns);
}
if (dates[i]==="") {
continue;
}
if (dates[i] instanceof Date && dates[i].getDate() == yesterday.getDate() && dates[i].getMonth() == yesterday.getMonth() ){
break;
}
}
colA=numbers.slice(-5)
var table = [];
Logger.log(rows.length)
Logger.log(colA)
for(var j=0;j<colA.length;j++)
{
for (var i = 0; i < rows.length;i++ )
{
table[i] =[];
table[i][0] = rows[i][colA[j]];
}
Logger.log("the number of the column is: "+colA[j]);
Logger.log(table);
// where I paste the data
destination = sheet.getRange(1,colA[j],table.length,1)
destination.setValues(table); // where we paste the values by column
}
It is important to make sure that the array is 2-dimensional and that its dimensions (rows and columns) correspond to the dimensions of the range into which you want to set the data.

Related

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

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

SOLVED - Copy row between Google Sheets using Google script

I need some help because I have zero knowledge of Google App Script programming language
and I'm not able to solve this problem; I searched the solution on different forums and I watched a lot of different videos on YouTube but I was not able to change the code as I need.
I'm combining Google Script with MIT App Inventor (this is the page from which I copied the code: https://ai2.metricrat.co.uk/guides/google-sheet-crudq-ii) and I need to change the "DELETE" function (the last one) to copy the row to delete into another sheet (called ORP_completati) in the same Google Sheet
function doGet(e) {
var ss = SpreadsheetApp.openById(e.parameter.ID);
var sh = ss.getSheetByName(e.parameter.SH);
var logSheet = ss.getSheetByName("ORP_completati");
var fn = e.parameter.FN;
var rg = sh.getDataRange().getValues();
const now = new Date();
// Create/Add new record, using comma separated values
if ( fn == 'CREATE' ) {
var data = e.parameter.DATA.split(',');
sh.appendRow(data);
return ContentService.createTextOutput("New record created");
}
// Reads/Returns all data as a stringified JSON List
else if ( fn == 'READ' ) {
return ContentService.createTextOutput(JSON.stringify(rg));
}
// Edit/Update existing record, requires index/row and current col1 to match
else if ( fn == 'UPDATE' ) {
var index = e.parameter.INDEX; //index in list
var col1 = e.parameter.COL1; // current/existing value of col1 - it could be replaced...
var data = e.parameter.DATA.split(','); //new data
var range = sh.getRange((parseInt(index)+1),1,1,data.length);
for (var i = 0; i < rg.length; i++ ) {
if ( index != undefined && i == index && col1 == rg[i][0] ) {
range.setValues([data]);
}
}
return ContentService.createTextOutput("Record updated");
}
// deletes a single record (and its row) from sheet. Requires row index and col1 to match
else if ( fn == 'DELETE' ) {
var index = e.parameter.INDEX; //index in list
var col1 = e.parameter.COL1; // current/existing value of col1 - it could be replaced...
for (var i = 0; i < rg.length; i++ ) {
if ( index != undefined && i == index && col1 == rg[i][0] ) {
sh.deleteRow(parseInt(index)+1);
}
}
return ContentService.createTextOutput("Versione 7 mod 9");
}
}
I hope everything is clear and I want to thank you in advance for your help
First, get the last column with content (you will need this in order to copy all the columns in the row)
var lastColumn = sh.getLastColumn()
Second, get the full row. getSheetValues takes 4 values: startRow, startColumn, number of rows and number of columns.
var copyRow = sh.getSheetValues(parseInt(index)+1, 1, 1, lastColumn)
Third, append the row to the logSheet sheet - Keep in mind that getSheetValues returns a 2D array and the appendRow function only takes a 1D array so you have to pass the first element.
logSheet.appendRow(copyRow[0])
Finally, you can delete the row
sh.deleteRow(parseInt(index)+1);
Resources:
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getSheetValues(Integer,Integer,Integer,Integer)
https://developers.google.com/apps-script/reference/spreadsheet/sheet#appendrowrowcontents

How to remove duplicate rows in Google Sheets using script

I currently have a column of data titled JobID. In this column, there are duplicates from an import that runs daily and grabs the latest data on the JobID's in question and appends them to the top of the sheet.
Therefore the most recent JobID rows are the ones with the data we need.
I'd like to know if there is a script that can be run on the sheet called 'History' to look up the column JobID, search every row below for duplicates and remove them, leaving the top, most recent JobID rows in the sheet.
I know that it is really easy to remove duplicates using the "Remove Duplicates" tool in Google Sheets... but I'm lazy and I'm trying to automate as much of this process as possible.
The script I have below runs without an error but is still not doing what I need it to. Wondering where I am going wrong here:
function removeDuplicates() {
//Get current active Spreadsheet
var sheet = SpreadsheetApp.getActive();
var history = sheet.getSheetByName("History");
//Get all values from the spreadsheet's rows
var data = history.getDataRange().getValues();
//Create an array for non-duplicates
var newData = [];
//Iterate through a row's cells
for (var i in data) {
var row = data[i];
var duplicate = false;
for (var j in newData) {
if (row.join() == newData[j].join()) {
duplicate = true;
}
}
//If not a duplicate, put in newData array
if (!duplicate) {
newData.push(row);
}
}
//Delete the old Sheet and insert the newData array
history.clearContents();
history.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
Remove Duplicate JobIDs
This function will keep the ones nearest to the top of the list. If you want to go the other way then resort the list in reverse order.
function removeDuplicates() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName("History");
var vA=sh.getDataRange().getValues();
var hA=vA[0];
var hObj={};
hA.forEach(function(e,i){hObj[e]=i;});//header title to index
var uA=[];
var d=0;
for(var i=0;i<vA.length;i++) {
if(uA.indexOf(vA[i][hObj['JobID']])==-1) {
uA.push(vA[i][hObj['JobID']]);
}else{
sh.deleteRow(i+1-d++);
}
}
}
Remove Duplicate JobIDs in Python
Based on Cooper's answer I wrote the same function in Python:
gsheet_id = "the-gsheet-id"
sh = gc.open_by_url("https://docs.google.com/spreadsheets/d/%s/edit#gid=0" % gsheet_id)
wks = sh[0]
def removeDuplicates(gwks):
headerRow = gwks[1]
columnToIndex = {}
i = 0
for column in headerRow:
columnToIndex[column] = i
i += 1
uniqueArray = []
d = 0
row_i = 0
for row in gwks:
row_i += 1
if gwks[row_i][columnToIndex['JobID']] not in uniqueArray:
uniqueArray.append(gwks[row_i][columnToIndex['JobID']])
else:
d += 1
gwks.delete_rows(row_i + 1 - d, 1)
removeDuplicates(wks)

Search a spreadsheet column for duplicate entries

I have some customer data in a Google Spreadsheet as shown below. New entries are added regularly, and I would like to use a script to search the contact number column to check if the contact number for a new entry already exists.
Also, if possible, I'd like the script to give some kind of notification that indicates the row number of the existing entry. For example, the notification could be a new column where it would just write the "row number" or "no entries found".
Data Available:
Contact Number Email ID Venue Address Service Date
1234567890 Test1#gmail.com cypress 21/04/2016
0123456789 Test2#gmail.com river run drive 22/04/2016
What I am looking for:
Contact Number Email ID Venue Address Service Date Duplicate entry in
1234567890 Test1#gmail.com cypress 21/04/2016 row1
0123456789 Test2#gmail.com river run drive 22/04/2016 no entries found
So, your description implies that you want to search for entry before it's input. Rather than searching for duplicates, which means there is already duplicate rows in your data. I have constructed a method for both for you.
This shows an example of both, this will either find the duplicates or it will find an entry by phone number. The remainder of the logic and customization is up to you.
Here is the example sheet for this: Example Spreadsheet
Edit: Added column finding logic, and added a button you can press to see the duplicates in the sheet. NOTE: You must be signed into a google account for the script to run
//Entry Point
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet(); //Get the current sheet
var dataRange = sheet.getDataRange();
var valuesRange = dataRange.getValues(); //The array of data on the sheet
var columns = GetColumns(valuesRange, dataRange.getNumColumns(), 0);
var duplicates = SearchForDuplicates(valuesRange, columns.columns['Contact Number'].index); //Search for existing duplicates
var elementIndex = SearchForValue(valuesRange, columns.columns['Contact Number'].index, 123456789); //Search for a phone number of 123456789
if(duplicates.length > 0){ //If there are duplicates
var isDuplicateColumn = columns.columns['Is Duplicate'].index;
for(var i = 0; i < duplicates.length; i++){
valuesRange[duplicates[i].index][isDuplicateColumn] = 'Yes'; //Assign Yes to the appropriate row
}
dataRange.setValues(valuesRange); //Set the spreadsheets values to that of the modified valuesRange
}
Logger.log(duplicates);
Logger.log(elementIndex);
}
//Searches an array for duplicate entries
function SearchForDuplicates(array, columnIndex){
var uniqueElements = {};
var duplicates = [];
for(var i = 0; i < array.length; i++){
if(typeof uniqueElements[array[i][columnIndex]] === 'undefined'){
uniqueElements[array[i][columnIndex]] = 0; //If the element does not yet exist in this object, add it
} else {
duplicates.push({row: array[i], index: i}); //If the element does exist, it's a duplicate
}
}
return duplicates;
}
//Searches an array for a value
function SearchForValue(array, columnIndex, value){
for(var i = 0; i < array.length; i++){
if(array[i][columnIndex] == value){
return i; //Element found, return index
}
}
return -1; //No element found, return -1
}
//Gets a columns object for the sheet for easy indexing
function GetColumns(valuesRange, columnCount, rowIndex)
{
var columns = {
columns: {},
length: 0
}
Logger.log("Populating columns...");
for(var i = 0; i < columnCount; i++)
{
if(valuesRange[0][i] !== ''){
columns.columns[valuesRange[0][i]] = {index: i ,value: valuesRange[0][i]};
columns.length++;
}
}
return columns;
}

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++)