I am trying to transfer rows of data from Sheet 1 to Sheet 2.
Thank you
I believe your goal is as follows.
Your script works fine. But, you want to reduce the process cost of your script.
In this case, how about the following modification?
Modified script:
In this modification, the rows are copied using setValues and deleted by Sheets API. By this, I thought that the process cost might be able to be reduced a little. So, please enable Sheets API at Advanced Google services before you use this script.
function archive() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var firstSheet = ss.getSheetByName("Sheet 1");
var firstSheetValues = firstSheet.getRange(1, 1, firstSheet.getLastRow(), firstSheet.getLastColumn()).getValues();
var sheetId = firstSheet.getSheetId();
var {values, requests} = firstSheetValues.reduce(function (o, r, i) {
if (r[1] == "Archive") {
o.values.push(r);
o.requests.push({ deleteDimension: { range: { sheetId, startIndex: i, endIndex: i + 1, dimension: "ROWS" } } });
}
return o;
}, { values: [], requests: [] });
var secondSheet = ss.getSheetByName("Sheet 2");
secondSheet.getRange(secondSheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
Sheets.Spreadsheets.batchUpdate({requests: requests.reverse()}, ss.getId());
}
References:
Method: spreadsheets.batchUpdate
DeleteDimensionRequest
Related
Is there a app script I can run that will update the ranges of all the filter views on a sheet at once?
I have hundreds of filter views, and it would be laborious to do it manually.
The filter views are all on a sheet called "Data'. I need to change the range from A1:AB3116 TO A1:AB9011
Thanks for any help.
I believe your goal is as follows.
You want to change the range of filter views.
You want to change from "A1:AB3116" to "A1:AB9011" of all filter views in "data" sheet.
In this case, how about the following sample script?
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet and enable Sheets API at Advanced Google services, and save the script.
Please confirm sheetName and obj. In this sample, your provided information is used.
function myFunction() {
var sheetName = "data"; // This is from your question.
var obj = [{ before: "A1:AB3116", after: "A1:AB9011" }]; // This is from your question.
// Retrieve spreadsheet and sheet.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = ss.getId();
var sheet = ss.getSheetByName(sheetName);
var sheetId = sheet.getSheetId();
// Convert a1Notation to gridRange.
var o = obj.map(({ before, after }) =>
[before, after].map(r => {
var rng = sheet.getRange(r);
var rowStart = rng.getRow() - 1;
var rowEnd = rowStart + rng.getNumRows();
var colStart = rng.getColumn() - 1;
var colEnd = colStart + rng.getNumColumns();
return { sheetId, startRowIndex: rowStart, endRowIndex: rowEnd, startColumnIndex: colStart, endColumnIndex: colEnd };
})
);
// Create request body for using the batchUpdate of Sheets API.
var filterViews = Sheets.Spreadsheets.get(spreadsheetId, { ranges: [sheetName], fields: "sheets(filterViews)" }).sheets[0].filterViews;
var requests = filterViews.reduce((ar, { range, ...e }) => {
var check = o.find(([{ startRowIndex, endRowIndex, startColumnIndex, endColumnIndex }]) => range.startRowIndex == startRowIndex && range.endRowIndex == endRowIndex && range.startColumnIndex == startColumnIndex && range.endColumnIndex == endColumnIndex);
if (check) {
ar.push({ updateFilterView: { filter: { filterViewId: e.filterViewId, range: check[1] }, fields: "*" } });
}
return ar;
}, []);
// Reuest Sheets API using the created request body.
if (requests.length == 0) return;
Sheets.Spreadsheets.batchUpdate({ requests }, spreadsheetId);
}
When this script is run, the filter views are retrieved from "data" sheet. And, the range of A1:AB3116 is searched from the retrieved filter views, and when it is found, the range is changed to A1:AB9011 and update the filter views.
In this sample, when you change multiple changes of ranges, you can use them in obj.
References:
Method: spreadsheets.get
Method: spreadsheets.batchUpdate
Related thread
Script to Update Multiple Google Sheet Filter View Ranges
I have a case that i want to delete blank rows for multiple sheets. I want to use Google Sheet API because the SpreadsheetApp method takes too long and i get the timeout error because of it. I have 10k+ rows.
I already made my own code (Spreadsheet method version):
function myFunction() {
const sheet = ['', '', '', '']; //sheets name
for (let i in sheet) {
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet[i]);
const maxrow = sheet.getMaxRows();
const lastrow = sheet.getLastRow();
if (maxrow - lastrow > 10) {
ss.deleteRows(lastrow + 10, maxrow - lastrow - 10);
}
}
}
I believe your goal is as follows.
You want to modify your showing script using Sheets API.
In your script, it seems that sheet is an array. So, I think that an error occurs at const maxrow = sheet.getMaxRows();. Please be careful about this.
In this case, how about the following modification?
Modified script:
Please enable Sheets API at Advanced Google services. Ref
function myFunction() {
const sheet = ['Sheet1', 'Sheet2',,,]; // Please set sheet names you want to use.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const requests = sheet.reduce((ar, s) => {
const sheet = ss.getSheetByName(s);
const maxrow = sheet.getMaxRows();
const lastrow = sheet.getLastRow();
if (maxrow - lastrow > 10) {
ar.push({ deleteDimension: { range: { sheetId: sheet.getSheetId(), startIndex: lastrow + 10, endIndex: maxrow, dimension: "ROWS" } } });
}
return ar;
}, []);
if (requests.length == 0) return;
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
References:
Method: spreadsheets.batchUpdate
DeleteDimensionRequest
I'm trying to build a Macro to erase all the rows that have empty values on column D. Originally, I was using this code that I found:
function deleteRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Datos Competidor 2 - EV');
var r = s.getRange('D:D');
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if(v[0,i]=='')
s.deleteRow(i+1);
};
However the excessive number of calls to the API made this really slow and some times even fail due to a timeout.
I decided to just add all the rows that met the condition to a list and then just pass that to the deleteRow() in order to only call the API once using this code:
function deleteBlankRows() {
emptyRange=[]
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Datos Competidor 2 - EV');
var r = s.getRange('D:D');
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if(v[0,i]=='')
emptyRange.push((i)+":"+(i));
ss.getRangeList(emptyRange).activate();
ss.getActiveSheet().deleteRows(ss.getActiveRange().getRow(), ss.getActiveRange().getNumRows());
};
The execution seems to work just fine, completing in 1 to 2 seconds, however, rows aren't erased as much as selected by the end of the execution.
This is what I see:
Final result
Any ideas why this is happening?
Thanks!
I believe your goal is as follows.
You want to reduce the process cost of your script.
In this case, how about using Sheets API? When Sheets API is used, I thought that the process cost for deleting the rows can be reduced a little. When the Sheets API is reflected in your script, it becomes as follows.
Modified script:
Before you use this script, please enable Sheets API at Advanced Google services.
function deleteRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Datos Competidor 2 - EV');
var values = s.getRange('D1:D' + s.getLastRow()).getDisplayValues();
var sheetId = s.getSheetId();
var requests = values.reduce((ar, [d], i) => {
if (!d) ar.push({ deleteDimension: { range: { sheetId, startIndex: i, endIndex: i + 1, dimension: "ROWS" } } });
return ar;
}, []).reverse();
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
References:
Method: spreadsheets.batchUpdate
DeleteDimensionRequest
Delete Rows with empties on column D
function deleteBlankRows() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const s = ss.getSheetByName('Datos Competidor 2 - EV');
const r = s.getRange('D1:D' + s.getLastRow());
const v = r.getValues().flat();
let d = 0;
v.forEach((e, i) => {
if (!e) {
s.deleteRow(i + 1 - d++)
}
})
}
I have the below code that checks through a sheet for the words "CASH". Once it's found, it will copy the row to a new sheet then delete the row.
Prior, my range for the values was set up as so:
var range = ss1.getRange(2, 1, lr, lc);
But now that my sheet has 6000+ rows, this is highly inefficient. I've changed it so that it only looks through a range that is lr-100 so that it doesn't have to dig so deep.
Since making that change, my old code to delete rows ss1.deleteRow(i+2) is no longer valid, because i references the row only within that particular range (i.e., if it's the 90th row out of that 100, i = 90, and that would end up deleting the 90th row in my sheet when it should have been, for example, 6500.
question How do I find the correct row# in this new way my script is setup?
var etfar = ["BOTZ"]
function doCash() {
for (var i = 0; i < etfar.length; i++) {
cashMoney(etfar[i])
}
};
function cashMoney(etf) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss1 = ss.getSheetByName(etf);
var ss2 = ss.getSheetByName("Cash");
var lr = ss1.getLastRow();
var lc = ss1.getLastColumn();
var range = ss1.getRange(lr-100, 1, lr, lc);
var data = range.getValues();
for (var i = data.length - 1; i >= 0; i--)
{
var check = data[i][4] // ith row, 3rd column
if (check.includes("CASH")) {
var rowUsd = data[i];
// something has to happen here to find the exact row
ss2.appendRow(rowUsd);
//ss1.deleteRow(i+2); <-- this is old code. Prior, in my 'range' I used to start my rows from "2", but that was highly inefficient and I've changed it to "lr-100" so that it begins checking from the last 100 rows instead
}
};
};
From now that my sheet has 6000+ rows, this is highly inefficient., in your situation, in order to reduce the process cost of the script, how about using Sheets API? When Sheets API is used for your situation, all rows might be able to be processed. When Sheets API is used for your script, it becomes as follows.
Modified script:
This script uses Sheets API. So, please enable Sheets API at Advanced Google services. And, please set etfar.
function sample() {
var etfar = ["Sheet1", "Sheet2",,,]; // Please set your sheet names.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dstSheet = ss.getSheetByName("Cash");
var dstSheetId = dstSheet.getSheetId();
var lastRow = dstSheet.getLastRow();
var { copyRows, deleteRows } = etfar.reduce((o, e) => {
var srcSheet = ss.getSheetByName(e);
var srcSheetId = srcSheet.getSheetId();
srcSheet.getRange("E2:E" + srcSheet.getLastRow()).getValues().forEach(([v], i) => {
if (v == "CASH") {
o.copyRows.push({ copyPaste: { source: { sheetId: srcSheetId, startRowIndex: i + 1, endRowIndex: i + 2, startColumnIndex: 0 }, destination: { sheetId: dstSheetId, startRowIndex: lastRow, endRowIndex: lastRow + 1, startColumnIndex: 0 } } });
o.deleteRows.push({ deleteDimension: { range: { sheetId: srcSheetId, dimension: "ROWS", startIndex: i + 1, endIndex: i + 2 } } });
lastRow++;
}
});
return o;
}, { copyRows: [], deleteRows: [] });
var requests = [...copyRows, ...deleteRows.reverse()];
if (requests.length == 0) return;
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
When this script is run, the values searched by the column "E" are retrieved from the sheets of etfar, and those values are appended to the destination sheet "Cash". And also, the copied rows of each sheet are removed.
In this script, these processes can be run by one API call.
Note:
When I saw your script, from var check = data[i][4] // ith row, 3rd column, I thought that data[i][4] is the coumn "E". But you say 3rd column. It's the column "C". If you want to search the value of "CASH" from the column "C", please modify getRange("E2:E" + srcSheet.getLastRow()) to getRange("C2:C" + srcSheet.getLastRow()).
References:
Method: spreadsheets.batchUpdate
CopyPasteRequest
DeleteDimensionRequest
I think this is what you want
function cashMoney(etf) {
var ss = SpreadsheetApp.getActive();
var sh1 = ss.getSheetByName(etf);
var sh2 = ss.getSheetByName("Cash");
var lr = sh1.getLastRow();
var lc = sh1.getLastColumn();
var range = sh1.getRange(lr - 100, 1, 101, lc);
var data = range.getValues();
var d = 0;
for (var i = data.length - 1; i >= 0; i--) {
var check = data[i][4] // ith row, 3rd column
if (check.includes("CASH")) {
var rowUsd = data[i];
sh2.appendRow(rowUsd);
sh.deleteRow(lr - 100 + i - d++);The row is data start row + i
}
};
}
The d keeps track of the rows that have already been deleted from the spreadsheet which is necessary because they have not been deleted from the data set.
I've been attempting to export the entire contents of my spreadsheet. The code works fine, but it displays an error message that says Maximum execution limit exceeded. According to my research, the maximum execution time is only 6 minutes. I'm new to this and am still trying to figure it out. Could you kindly help me in determining a possible solution for this?
Here's the code that I'm currently using...
function PrintMultiple() {
const srcSs = SpreadsheetApp.getActiveSpreadsheet();
const sheet = srcSs.getSheetByName("TEMPLATE");
const values = sheet.getRange("C2").getDataValidation().getCriteriaValues()[0].getValues().flat().filter(String);
const dstSs = SpreadsheetApp.create("tempSpreadsheet");
SpreadsheetApp.getActive().toast("About to take some action... Please wait...");
values.forEach(v => {
sheet.getRange("C2").setValue(v);
SpreadsheetApp.flush();
const tempSheet = sheet.copyTo(srcSs);
const range = tempSheet.getDataRange();
range.copyTo(range, {contentsOnly: true});
tempSheet.getRange("B2:2").clear().clearDataValidations();
tempSheet.getDrawings().forEach(e => e.remove());
tempSheet.deleteColumn(1);
tempSheet.deleteRow(1);
tempSheet.deleteRow(2);
tempSheet.deleteRow(3);
tempSheet.copyTo(dstSs);
srcSs.deleteSheet(tempSheet);
});
dstSs.deleteSheet(dstSs.getSheets()[0]);
}
I believe your goal is as follows.
You want to reduce the process cost of your script.
From your script, when the length of values is large, I thought that the process of copy might be the high cost. So, in this case, how about using Sheets API? I thought that when Sheets API is used, the process cost of your script might be able to be reduced. When Sheets API is used for your script, it becomes as follows.
Modified script:
Before you use this script, please enable Sheets API at Advanced Google services.
function PrintMultiple() {
const ss = SpreadsheetApp.getActiveSpreadsheet().copy("tempSpreadsheet");
const sheet = ss.getSheetByName("TEMPLATE");
const sourceSheetId = sheet.getSheetId();
sheet.getDrawings().forEach(e => e.remove());
const values = sheet.getRange("C2").getDataValidation().getCriteriaValues()[0].getValues().flat().filter(String);
const requests1 = values.flatMap((v, i) => {
const sheetId = 123456 + i;
return [
{ duplicateSheet: { newSheetName: `page${i + 1}`, sourceSheetId, newSheetId: sheetId, insertSheetIndex: i + 2 } },
{ updateCells: { range: { sheetId: sheetId, startRowIndex: 1, endRowIndex: 2, startColumnIndex: 2, endColumnIndex: 3 }, rows: [{ values: [{ userEnteredValue: { numberValue: v } }] }], fields: "userEnteredValue.numberValue" } },
];
});
Sheets.Spreadsheets.batchUpdate({ requests: requests1 }, ss.getId());
const requests2 = values.flatMap((_, i) => {
const sheetId = 123456 + i;
return [
{ copyPaste: { source: { sheetId }, destination: { sheetId }, pasteType: "PASTE_VALUES" } },
{ deleteDimension: { range: { sheetId, startIndex: 0, endIndex: 3, dimension: "ROWS" } } },
{ deleteDimension: { range: { sheetId, startIndex: 0, endIndex: 1, dimension: "COLUMNS" } } },
];
});
Sheets.Spreadsheets.batchUpdate({ requests: requests2 }, ss.getId());
ss.deleteSheet(sheet);
ss.deleteSheet(ss.getSheets()[0]);
}
In this modification, the method of batchUpdate of Sheets API is used for copying values and deleting rows and columns.
When this script is run, "tempSpreadsheet" Spreadsheet is created to the root folder.
Reference:
Method: spreadsheets.batchUpdate