Update all filter view ranges - google-apps-script

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

Related

Copy from template to rangelist

function FunctionC12C31() {
var allSheetTabs,i,L,thisSheet,thisSheetName,sheetsToExclude,value;
sheetsToExclude = ['Template','Sheet1','Sheet2'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
allSheetTabs = ss.getSheets();
L = allSheetTabs.length;
for (i=0;i<L;i++) {
thisSheet = allSheetTabs[i];
thisSheetName = thisSheet.getName();
//continue to loop if this sheet is one to exclude
if (sheetsToExclude.indexOf(thisSheetName) !== -1) {continue;}
value = thisSheet.getRangeList(['C12:C35','C5:C6','G16:G19','G4']).clearContent();
}}
Need help modifying this to instead of clearing contents
it will copy the formula from a RangeList(['C12:C35','C5:C6','G16:G19','G4'])
to be copied to from a Template Sheet too all sheet except to "sheetsToExclude" list.
or perhaps a different script?
copy from a template sheet RangeList(['C12:C35','C5:C6','G16:G19','G4'])
data will be formulas and need to be pasted on same RangeList
paste data to ALL sheets except from specified sheets. Like sheetsToExclude = ['Template','Sheet1','Sheet2'];
I believe your goal is as follows.
You want to retrieve the formulas from the cells 'C12:C35','C5:C6','G16:G19','G4' of a template sheet. And, you want to put the retrieved formulas to the same ranges in the sheets except for sheetsToExclude = ['Template','Sheet1','Sheet2'] in the active Spreadsheet.
The template sheet is included in the same active Spreadsheet. The sheet name is "Template".
In this case, how about the following modification?
Modified script 1:
Unfortunately, in the current stage, the values cannot be retrieved and put from the discrete cells using RangeList. So, in this case, it is required to use another method. In order to retrieve the formulas from the discrete cells and put the formulas to the discrete cells with the row process cost, I would like to propose using Sheets API. So, before you use this script, please enable Sheets API at Advanced Google services.
In this script, "Method: spreadsheets.batchUpdate" is used.
function myFunction() {
// These variables are from your showing script.
var templateSheetName = "Template";
var sheetsToExclude = [templateSheetName, 'Sheet1', 'Sheet2'];
var ranges = ['C12:C35', 'C5:C6', 'G16:G19', 'G4'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var template = ss.getSheetByName(templateSheetName);
var sheetId = template.getSheetId();
var gridRanges = ranges.map(r => {
var range = template.getRange(r);
var row = range.getRow();
var col = range.getColumn();
return {
sheetId,
startRowIndex: row - 1,
endRowIndex: row - 1 + range.getNumRows(),
startColumnIndex: col - 1,
endColumnIndex: col - 1 + range.getNumColumns(),
};
});
var requests = ss.getSheets().reduce((ar, s) => {
if (!sheetsToExclude.includes(s.getSheetName())) {
gridRanges.forEach(source => {
var destination = JSON.parse(JSON.stringify(source));
destination.sheetId = s.getSheetId();
ar.push({ copyPaste: { source, destination, pasteType: "PASTE_FORMULA" } });
});
}
return ar;
}, []);
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
}
When this script is run, the formulas are retrieved from 'C12:C35', 'C5:C6', 'G16:G19', 'G4' of templateSheetName sheet, and the retrieved formulas are put into the same ranges in the sheets except for sheetsToExclude.
Modified script 2:
In this script, "Method: spreadsheets.values.batchGet" and "Method: spreadsheets.values.batchUpdate" are used.
function myFunction() {
// These variables are from your showing script.
var templateSheetName = "Template";
var sheetsToExclude = [templateSheetName, 'Sheet1', 'Sheet2'];
var ranges = ['C12:C35', 'C5:C6', 'G16:G19', 'G4'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var sheets = ss.getSheets().filter(s => !sheetsToExclude.includes(s.getSheetName()));
var formulas = Sheets.Spreadsheets.Values.batchGet(ssId, { ranges: ranges.map(r => `'${templateSheetName}'!${r}`), valueRenderOption: "FORMULA" }).valueRanges;
var data = sheets.flatMap(s => formulas.slice().map(({ range, values }) => ({ range: range.replace(templateSheetName, s.getSheetName()), values })));
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ssId);
}
When this script is run, the formulas are retrieved from 'C12:C35', 'C5:C6', 'G16:G19', 'G4' of templateSheetName sheet, and the retrieved formulas are put into the same ranges in the sheets except for sheetsToExclude.
Note:
If you cannot use Sheets API, how about the following modified script? In this case, only the Spreadsheet service (SpreadsheetApp) is used.
function myFunction2() {
// These variables are from your showing script.
var templateSheetName = "Template";
var sheetsToExclude = [templateSheetName, 'Sheet1', 'Sheet2'];
var ranges = ['C12:C35', 'C5:C6', 'G16:G19', 'G4'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var sheets = ss.getSheets().filter(s => !sheetsToExclude.includes(s.getSheetName()));
var formulas = ranges.map(r => ss.getSheetByName(templateSheetName).getRange(r).getFormulas());
sheets.forEach(s => ranges.forEach((r, i) => s.getRange(r).setFormulas(formulas[i])));
}
References:
Method: spreadsheets.batchUpdate
Method: spreadsheets.values.batchGet
Method: spreadsheets.values.batchUpdate

How to delete blank rows for multiple sheets using google API

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

How do I populate Google Form questions using column A data from different Google Sheets Tabs using Apps Script?

I am trying to populate Google Form questions from a Google Sheet workbook using Apps Script. The issue I'm running into is that the Form isn't populating and I'm getting the error TypeError: Cannot read property '0' of undefined specifically on the line googleSheetsQuestions[0]. How can I populate the 2 questions on my Google Form using only column A values from both Sheets?
function openForm(e)
{
populateQuestions();
}
function populateQuestions() {
var form = FormApp.getActiveForm();
var googleSheetsQuestions = getQuestionValues();
var itemsArray = form.getItems();
itemsArray.forEach(function(item){
googleSheetsQuestions[0].forEach(function(header_value, header_index) {
if(header_value == item.getTitle())
{
var choiceArray = [];
for(j = 1; j < googleSheetsQuestions.length; j++)
{
(googleSheetsQuestions[j][header_index] != '') ? choiceArray.push(googleSheetsQuestions[j][header_index]) : null;
}
item.asCheckboxItem().setChoiceValues(choiceArray);
}
});
});
}
function getQuestionValues() {
var ss= SpreadsheetApp.openById('1234567890');
["Sheet1", "Sheet2"].forEach(function (s) {
var questionSheet = ss.getSheetByName(s);
var returnData = questionSheet.getDataRange().getValues();
debugger;
return returnData;
})
}
In your situation, how about the following sample script?
Sample script:
function populateQuestions() {
// Retrieve values from Google Spreadsheet.
var ss = SpreadsheetApp.openById('1234567890');
var obj = ["Sheet1", "Sheet2"].reduce((o, s) => {
var sheet = ss.getSheetByName(s);
var [h, ...values] = sheet.getRange("A1:A" + sheet.getLastRow()).getValues();
o[h] = values;
return o;
}, {});
// Put values to the Google Form.
var form = FormApp.getActiveForm();
var itemsArray = form.getItems();
itemsArray.forEach(e => {
var item = e.asCheckboxItem();
var title = item.getTitle();
if (obj[title]) {
item.setChoiceValues(obj[title]);
}
});
}
When this script is run, the values are retrieved from 2 sheets of Google Spreadsheet, and the retrieved values are put into the Google Form.
In your showing script, googleSheetsQuestions of var googleSheetsQuestions = getQuestionValues(); is always undefined. From your goal, in this case, I used an object for searching the title of the question.
Note:
In this sample script, please confirm whether the title of Google Form is the same with the values of "A1" of each sheet, again.
References:
reduce()
forEach()

Google Sheets deleteRow not working in my Macro

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

Google Sheet App Script - How to only setValues only unique values

In this spreadsheet: https://docs.google.com/spreadsheets/d/1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI/edit#gid=0
I'd like to Combine only unique Links and Category into Combined sheet.
Right now, with my script, it can only combine all existing data:
function combine() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
var sourceID = '1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI';
var targetID = '1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI';
var sheetExclude = ["Combined"];
var sheetExcludeIndex = new Array(sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
for (var e in sheetExclude) {
if (String(sheet.getName() == sheetExclude[e])) {
sheetExcludeIndex[e] = sheet.getIndex;
}
}
}
allsheets.splice(sheetExcludeIndex, sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
}
function updateSourceToTarget(sourceID, sourceName, targetID, targetname) {
Logger.log(sourceID + ' ' + sourceName + ' ' +targetname);
var source = SpreadsheetApp.openById(sourceID).getSheetByName(sourceName);
var destination = SpreadsheetApp.openById(targetID).getSheetByName(targetname);
var sourcelastRow = source.getLastRow();
var sourcelastCol = source.getLastColumn();
var destinationlastRow = destination.getLastRow();
var destinationlastCol = destination.getLastColumn();
var sourcedata = source.getRange(2, 9, sourcelastRow, 10).getValues();
destination.getRange(destinationlastRow + 1, 2, sourcelastRow, sourcelastCol).setValues(sourcedata);
}
However, I'd like to only combine unique links from Sheet2 and Sheet3:
In red is unique data
Sheet2:
Sheet3:
How can I efficiently add only unique values to Combined from Sheet2& Sheet3?
You want to put the values, which removed the duplicated links, to the target sheet (in this case, it's Combined sheet.).
The duplicated links are checked from the target sheet and source sheets. In this case, the target sheet and source sheets are Combined, Sheet2 and Sheet3, respectively.
In your sample Spreadsheet, you want to put the following rows to the target sheet.
https://thehill.com/policy/national-security/department-of-homeland-security/460158-new-hampshire-border-patrol BorderSecurity
https://abcnews.go.com/International/climate-change-frontier-worlds-northernmost-town/story?id=65381362 ClimateChange
You want to achieve this by modifying your Google Apps Script.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modified script:
Please modify your script as follows. In this modification, your function of updateSourceToTarget() is not used.
From:
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
To:
// Retrieve values from the target sheet.
var targetSheet = ss.getSheetByName(sheetExclude[0]);
var targetValues = targetSheet.getRange("B2:C" + targetSheet.getLastRow()).getValues();
// Retrieve values from all source sheets. <--- Modified
var sourceValues = allsheets.reduce(function(ar, sheet) {
var v = sheet.getRange(2, 9, sheet.getLastRow() - 1, 10).getValues().filter(function(r) {return r[0] && r[1]});
if (v.length > 0) {
v = v.filter(function(e) {return !ar.some(function(f) {return e[0] === f[0]})});
Array.prototype.push.apply(ar, v);
}
return ar;
}, []);
// Remove the duplication values between the target sheet and all source sheets.
var dstValues = sourceValues.filter(function(e) {return !targetValues.some(function(f) {return e[0] === f[0]})});
// Add the result values to the target sheet.
if (dstValues.length > 0) {
var destination = SpreadsheetApp.openById(targetID).getSheetByName(sheetExclude[0]);
destination.getRange(destination.getLastRow() + 1, 2, dstValues.length, dstValues[0].length).setValues(dstValues);
}
The flow of this modified script is as follows.
Retrieve values from the target sheet.
Retrieve values from all source sheets.
Remove the duplication values between the target sheet and all source sheets.
Add the result values to the target sheet.
Note:
When your shared Spreadsheet is used as the target (Combined) and source sheets (Sheet2 and Sheet3), the following rows are added to the target sheet.
https://thehill.com/policy/national-security/department-of-homeland-security/460158-new-hampshire-border-patrol BorderSecurity
https://abcnews.go.com/International/climate-change-frontier-worlds-northernmost-town/story?id=65381362 ClimateChange
References:
reduce()
filter()
some()
If I misunderstood your question and this was not the direction you want, I apologize.
Added:
In this additional script, a hash table is used for this situation, as mentioned by TheMaster's comment. For example, a sample can be also seen at this thread. In your situation, at first, all values are retrieved from all sheets including Combined sheet, and the hash table is created. By this, the duplicated values are removed. Then, the converted values to an array are put to the Spreadsheet.
Sample script:
Please modify your script as follows.
From:
allsheets.splice(sheetExcludeIndex, sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
To:
// allsheets.splice(sheetExcludeIndex, sheetExclude.length); // In this script, this line is not used.
// Retrieve values from the target sheet.
var targetSheet = ss.getSheetByName(sheetExclude[0]);
var targetValues = targetSheet.getRange("B2:C" + targetSheet.getLastRow()).getValues();
// Retrieve values from all source sheets.
// Remove the duplication values between the target sheet and all source sheets.
var sourceValues = allsheets.reduce(function(obj, sheet) {
var v = sheet.getRange(2, 9, sheet.getLastRow() - 1, 10).getValues().filter(function(r) {return r[0] && r[1]});
if (v.length > 0) v.forEach(function(e) {if (!(e[0] in obj)) obj[e[0]] = e[1]});
return obj;
}, {});
var dstValues = Object.keys(sourceValues).map(function(e) {return [e, sourceValues[e]]});
// Add the result values to the target sheet.
if (dstValues.length > 0) {
var destination = SpreadsheetApp.openById(targetID).getSheetByName(sheetExclude[0]);
destination.getRange(2, 2, destination.getLastRow(), 2).clearContent();
destination.getRange(2, 2, dstValues.length, dstValues[0].length).setValues(dstValues);
}
The proposed 2 sample scripts can be used for your situation. So please select one of them.