Script to automatically change multiple ranges of links - google-apps-script

The "master" table has links to cells within that table. These are "I6:I10", "I212:I216", "I418:I422", "I625:I629", "I832:I836", "I1038:I1042".
How can I change these references so that when creating a copy of the "master" table, they refer to cells inside the "master copy"?
Maestro Tanaike wrote a script that works. But I wrote a little incorrectly what is required of the script. Need to modify it so that it processes the ranges "I6:I10", "I212:I216", "I418:I422", "I625:I629", "I832:I836", "I1038:I1042"
function myFunction() {
const checkRange = "I6:I10"; // This is from yoru sample Spreadsheet.
const sheet = SpreadsheetApp.getActiveSheet();
const sheetId = sheet.getSheetId();
const range = sheet.getRange(checkRange);
const richTextValues = range.getRichTextValues().map(r => r.map(c => {
const link = c.getLinkUrl();
return link ? c.copy().setLinkUrl(link.replace(/#gid\=.*&/, `#gid=${sheetId}`)).build() : c;
}));
range.setRichTextValues(richTextValues);
}
Link to table - https://docs.google.com/spreadsheets/d/1Jrpjm7Yjwp-3WTFKyBz87laPeq6ksjwPaaiWWNcIoow/edit?usp=sharing
i tried to put in a script
const checkRange2 = "I212:I216";
but it doesn't work

Use a RangeList, like this:
function replace() {
const sheet = SpreadsheetApp.getActiveSheet();
const sheetId = sheet.getSheetId();
const rangeList = sheet.getRangeList(['I6:I10', 'I212:I216', 'I418:I422', 'I625:I629', 'I832:I836', 'I1038:I1042']);
rangeList.getRanges().forEach(range => modifyLinks_(range, sheetId));
}
function modifyLinks_(range, sheetId) {
const richTextValues = range.getRichTextValues().map(row => row.map(value => {
const link = value.getLinkUrl();
return link ? value.copy().setLinkUrl(link.replace(/#gid\=.*&/, `#gid=${sheetId}`)).build() : value;
}));
range.setRichTextValues(richTextValues);
}

Related

Copying a row from one sheet to another and deleting the original data

I am trying to copy the last populated row from one spreadsheet, to the first non populated row of another spreadsheet. And if possible delete the row that was copied from the source spreadsheet.
function moveRows () {
importRange(
"1PugO3VL8ZkGzA6stYfUkQK4WoU-FfRUBHUzRDTsEbYE",
"Sheet2!A:Z",
"1A2buwUnoDYVRILSETTFd_v6XGmrQtsGD-nT4P8Ufl_0",
"Sheet x!B1"
);
}
function importRange(sourceID,sourceRange,destinationID,destinationRangeStart) {
const sourceSS = SpreadsheetApp.openById(sourceID);
const sourceRng = sourceSS.getRange(sourceRange)
const sourceVals = sourceRng.getValues();
// console.log(sourceVals);
const destinationSS = SpreadsheetApp.openById(destinationID);
const destStartRange = destinationSS.getRange(destinationRangeStart);
const destSheet = destinationSS.getSheetByName(destStartRange.getSheet().getName());
const destRange = destSheet.getRange(
destStartRange.getLastRow() + 1 ,
destStartRange.getColumn(),
sourceVals.length,
sourceVals[0].length
);
destRange.setValues(sourceVals);
};
I found this online, but it copies the entire data from one sheet to another. It does the job, but not exactly what I need it to do.
This should work.
Instead of deleting a row from the original sheet, this will set a row of empty values into the copied range.
Just replace the sheet name of the oriSheet and tarSheet according your spreadsheet, should get the job done.
function moveRow() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const oriSheet = ss.getSheetByName('original');
const tarSheet = ss.getSheetByName('target');
const oriRange = oriSheet.getRange(oriSheet.getLastRow(),1,1,oriSheet.getLastColumn());
const values = oriRange.getValues();
tarSheet.getRange(tarSheet.getLastRow()+1,1,1,values[0].length).setValues(values);
oriRange.setValues([values[0].map(col => '')]);
}
Just realized that you are working on 2 spreadsheets...
const ssid_1 = '1PugO3VL8ZkGzA6stYfUkQK4WoU-FfRUBHUzRDTsEbYE';
const ssid_2 = '1A2buwUnoDYVRILSETTFd_v6XGmrQtsGD-nT4P8Ufl_0';
function moveRow() {
const oriSheet = SpreadsheetApp.openById(ssid_1).getSheetByName('original');
const tarSheet = SpreadsheetApp.openById(ssid_2).getSheetByName('target');
const oriRange = oriSheet.getRange(oriSheet.getLastRow(),1,1,oriSheet.getLastColumn());
const values = oriRange.getValues();
tarSheet.getRange(tarSheet.getLastRow()+1,1,1,values[0].length).setValues(values);
oriRange.setValues([values[0].map(col => '')]);
}
Just add the Spreadsheet IDs into the variables on top and it should work.

Google Apps Script - How to Update Code to Get Unique Values From Column And Filter Based On Them Instead of Manually Inputting Them

I currently have some code (pasted below) that I use in order to take subsets of a dataset and paste them in separate tabs in the same Google Sheets file. I'm currently manually inputting the values with which I filter the dataset in a list and then looping though each value in the list. I would like to update the code to look through the column and pick up on the unique values in the column and turn the unique values into a list that I would then look through using the rest of the code. I'm having trouble figuring out how to do this though.
Here is a link to the image of my dataset:
enter image description here
Below is my code. I would really like to update the const list = "" part to not be manually inputted anymore but to grab the unique values from the Product Type column (column # 4).
function getSubsetDataComplaints() {
const shName = "RawData";
const list = ["Toy Cars", "Barbie Dolls", "Videogames", "Role Playing Games","Trading Card Games","Food"];
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [headers, ...values] = ss.getSheetByName(shName).getDataRange().getValues()
list.forEach(elem => {
const result = [headers, ...values.filter(r => r[3].includes(elem))]
const sheet = ss.insertSheet(elem);
sheet.getRange(1,1, result.length, result[0].length).setValues(result);
})
}
Try
const shName = "RawData";
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [headers, ...values] = ss.getSheetByName(shName).getDataRange().getValues()
const list = values.map(r => r[3]).flat().filter(onlyUnique).sort()
and add this function
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
your complete code
function getSubsetDataComplaints() {
const shName = "RawData";
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [headers, ...values] = ss.getSheetByName(shName).getDataRange().getValues()
const list = values.map(r => r[3]).flat().filter(onlyUnique).sort()
list.forEach(elem => {
try {
if (elem != '') {
const result = [headers, ...values.filter(r => r[3].includes(elem))]
const sheet = ss.insertSheet(elem);
sheet.getRange(1, 1, result.length, result[0].length).setValues(result);
}
} catch (e) {
Browser.msgBox(e)
}
})
}
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}

App Script: How to set the values of each cell using the .getRangeList?

I am using the .SetValues to attempt to fill every cell I selected through this line var targetSheetRange = targetSheet.getRangeList(arr1);
Unfortunately, when I do it, it always returns me the value of the first cell on all the remaining cells in my Target sheet instead of setting the value of each individual cell from the Source Sheet.
Here's my code:
function filtersCopyData() {
var dTable = SpreadsheetApp.getActiveSpreadsheet();
var sSheetDay = dTable.getSheetByName('Day 1'); // Source Sheet
var sheetRange = sSheetDay.getRangeList(['K3','K4','K5','K6','K7']).getRanges().map(range => range.getValues());
var targetSheet = dTable.getSheetByName('All Filters report'); // Target Sheet
var arr1 = ['B4:C4', 'B6:C6', 'B7:C7', 'B9:C9', 'B10:C10'];
var targetSheetRange = targetSheet.getRangeList(arr1);
targetSheetRange.setValue(sheetRange);
}
K3 value is 9, K4 value is 20, K5 value is 10, K6 value is 10, and K7 value is 10.
targetSheetRange.setValue(sheetRange); When this code is run, all the cells in arr1 return the value of 9, instead of copying the value of each cell from the Source Sheet.
I hope this thing that I'm trying to accomplish does make sense on the code, PS. I'm really a beginner. Thank you everyone!
Description
RangeList is an Array not a Range. I'm suprised your script even ran. You have to use Array.forEach to set the values of the non-contiguous ranges.
You are creating a RangeList and then getting the A1Notation of each range. You can simply define an array of the range A1Notations.
Note, some of the names are different than yours for my test sheet.
Script
function test() {
try {
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("Data");
var rangeList = ["A1","A3","A5"];
var values = rangeList.map( range => sheet.getRange(range).getValue() );
rangeList = ["C1:D1","C3:D3","C5:D5"];
rangeList.forEach( (range,index) => sheet.getRange(range).setValues([[values[index],values[index]]]));
}
catch(err) {
console.log(err);
}
}
Reference
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getRangeList(String)
https://developers.google.com/apps-script/reference/spreadsheet/range-list#getRanges()
https://www.w3schools.com/jsref/jsref_map.asp
https://www.w3schools.com/jsref/jsref_foreach.asp
Writing to a rangelist with a rangelist of values
function filtersCopyData() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0'); // Source Sheet
const vs = sh.getRangeList(['K3','K4','K5','K6','K7']).getRanges().map(range => range.getValue());
const tsh = ss.getSheetByName('Sheet1');
const arr1 = ['B4:C4', 'B6:C6', 'B7:C7', 'B9:C9', 'B10:C10'];
const rgl = breakUpRangeList(ss,tsh,tsh.getRangeList(arr1));
const l = rgl.getRanges().length;
rgl.getRanges().forEach((r,i) => {
let a1 = r.getA1Notation();
let idx = i % arr1.length;
r.setValue(vs[idx]);
});
}
function breakUpRangeList(ss=SpreadsheetApp.getActive(),sh=ss.getSheetByName("Sheet0"),rgl) {
let b = [];
rgl.getRanges().forEach(rg => {
rg.getValues().forEach((r,i) => {
let row = rg.getRow() + i;
r.forEach((c, j) => {
let col = rg.getColumn() + j;
b.push(sh.getRange(row, col).getA1Notation())
})
})
})
//Logger.log(JSON.stringify(b));
return sh.getRangeList(b);
}

App Script: What's the shorter way to get the range of multiple cells

I am trying to copy the data from the Source sheet to my Target sheet. Is there a version where I can shorten this code? It seems to be that the .getRange can only accept one string.
When I try this line of code const sRange = sSheetDay.getRange('I4', '15', 'I6'); and so on, it always says it can't find the range. I know my questioning is not that good, especially I'm really a beginner at this. Hopefully someone has the same experience.
My main goal to this is to copy the data from a specific cell. And I can't seem to find the solution.
function copyDataFilters() {
const dTable = SpreadsheetApp.getActiveSpreadsheet();
const sSheetDay = dTable.getSheetByName('Day 1');
const sRange = sSheetDay.getRange('I4');
const sRange2 = sSheetDay.getRange('I5');
const sRange3 = sSheetDay.getRange('I6');
const sRange4 = sSheetDay.getRange('I7');
const sRange5 = sSheetDay.getRange('I8');
const sRange6 = sSheetDay.getRange('I9');
const sRange7 = sSheetDay.getRange('I12');
const sRange8 = sSheetDay.getRange('I13');
const sRange9 = sSheetDay.getRange('I16');
const sRange10 = sSheetDay.getRange('I17');
const sRange11 = sSheetDay.getRange('I20');
const sRange12 = sSheetDay.getRange('I21');
const sRange13 = sSheetDay.getRange('I24');
const sRange14 = sSheetDay.getRange('I25');
const sRange15 = sSheetDay.getRange('I27');
const sValue = sRange.getValues();
const sValue2 = sRange2.getValues();
const sValue3 = sRange3.getValues();
const sValue4 = sRange4.getValues();
const sValue5 = sRange5.getValues();
const sValue6 = sRange6.getValues();
const sValue7 = sRange7.getValues();
const sValue8 = sRange8.getValues();
const sValue9 = sRange9.getValues();
const sValue10 = sRange10.getValues();
const sValue11 = sRange11.getValues();
const sValue12 = sRange12.getValues();
const sValue13 = sRange13.getValues();
const sValue14 = sRange14.getValues();
const sValue15 = sRange15.getValues();
const targetSheet = dTable.getSheetByName('All filters report');
const targetRange = targetSheet.getRange('B4:C4');
const targetRange2 = targetSheet.getRange('B5:C5');
const targetRange3 = targetSheet.getRange('B6:C6');
const targetRange4 = targetSheet.getRange('B7:C7');
const targetRange5 = targetSheet.getRange('B8:C8');
const targetRange6 = targetSheet.getRange('B9:C9');
const targetRange7 = targetSheet.getRange('B11:C11');
const targetRange8 = targetSheet.getRange('B12:C12');
const targetRange9 = targetSheet.getRange('B14:C14');
const targetRange10 = targetSheet.getRange('B15:C15');
const targetRange11 = targetSheet.getRange('B17:C17');
const targetRange12 = targetSheet.getRange('B18:C18');
const targetRange13 = targetSheet.getRange('B20:C20');
const targetRange14 = targetSheet.getRange('B21:C21');
const targetRange315 = targetSheet.getRange('B24:C24');
targetRange.setValue(sValue);
targetRange2.setValue(sValue2);
targetRange3.setValue(sValue3);
targetRange4.setValue(sValue4);
targetRange5.setValue(sValue5);
targetRange6.setValue(sValue6);
targetRange7.setValue(sValue7);
targetRange8.setValue(sValue8);
targetRange9.setValue(sValue9);
targetRange10.setValue(sValue10);
targetRange11.setValue(sValue11);
targetRange12.setValue(sValue12);
targetRange13.setValue(sValue13);
targetRange14.setValue(sValue14);
targetRange315.setValue(sValue15);
}
Use getRangeList
From the references
// Get a list of ranges A1:D4, F1:H4.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var rangeList = sheet.getRangeList(['A1:D4', 'F1:H4']);
References
https://developers.google.com/apps-script/reference/spreadsheet/sheet?hl=en#getrangelista1notations
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=en#getrangelista1notations
function elfunko() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
//you can do this with values
const [v4,v5,v6,v7,v8,v9,,,v12,v13,,,v16,v17,v18,v19,v20,v21,,,v24,v25,v26,v27] = sh.getRange("I4:I27").getValues();
Logger.log(v4);
//unfortunatey you cannot do this with ranges. It resullts in an error
const [I4,I5,I6,I7,I8,I9,,,I12,I13,,,I16,I17,I18,I19,I20,I21,,,I24,I25,I26,I27] = sh.getRange("I4:I27");
Logger.log(I4.getValue());
}
So were stuck with handling multiple ranges with a rangelist as shown Ruben's answer
In the current stage, when the different values are trying to be got from and put to the scattered cells, the Spreadsheet service (SpreadsheetApp) has no methods for directly achieving this goal. So, in order to directly achieve this goal, as the current workaround, Sheets API can be used. When this is reflected in a sample script by including the goal of your script, it becomes as follows.
Sample script:
Before you use this script, please enable Sheets API at Advanced Google services.
function copyDataFilters2() {
const srcSheet = "Day 1";
const dstSheet = "All filters report";
const src = ["I4", "I5", "I6", "I7", "I8", "I9", "I12", "I13", "I16", "I17", "I20", "I21", "I24", "I25", "I27"].map(e => `'${srcSheet}'!${e}`);
const dst = ["B4:C4", "B5:C5", "B6:C6", "B7:C7", "B8:C8", "B9:C9", "B11:C11", "B12:C12", "B14:C14", "B15:C15", "B17:C17", "B18:C18", "B20:C20", "B21:C21", "B24:C24"].map(e => `'${dstSheet}'!${e}`);
const ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
const values = Sheets.Spreadsheets.Values.batchGet(ssId, { ranges: src }).valueRanges.map(({ values }) => values ? [Array(2).fill(values[0][0])] : [[""]]);
const data = values.map((e, i) => ({ values: e, range: dst[i] }));
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ssId);
}
When this script is run, the values are retrieved from the cells "I4", "I5", "I6", "I7", "I8", "I9", "I12", "I13", "I16", "I17", "I20", "I21", "I24", "I25", "I27" of "Day 1" sheet, and the retrieved values are put to the cells of "B4:C4", "B5:C5", "B6:C6", "B7:C7", "B8:C8", "B9:C9", "B11:C11", "B12:C12", "B14:C14", "B15:C15", "B17:C17", "B18:C18", "B20:C20", "B21:C21", "B24:C24".
References:
map()
Method: spreadsheets.values.batchGet
Method: spreadsheets.values.batchUpdate

Change range in ConditionalFormatRuleBuilder

Goal: Change range (sheetname, not the a1Notation) inside a ConditionalFormatRuleBuilder.copy()
Error: Conditional format rule cannot reference a different sheet.
I am trying to use the copy method thats is (not so) explained. With the copy i know i have al the arguments for the new conditional formatting i need. Only thing i need to change is the sheetname. Add ranges is working fine, but change/clear the ranges i can't seem to figure out. I found a post, but i want it to make more generic. This example is fine if you know the conditions to work with.
In the docs there is also a .build() is that a option i need in implement?
MainFunction:
function copyFormattingToTargets() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
//const input = SpreadsheetApp.getUi().prompt("Copy formatting from:").getResponseText();
const input = 'Data';
const targets = ['Log','Test'];
const templateSheet = ss.getSheetByName(input);
const inputRules = templateSheet.getConditionalFormatRules();
const rules = convertRules(inputRules,targets);
targets.forEach(target => {
const sheet = ss.getSheetByName(target);
target.clearConditionalFormatRules();
target.setConditionalFormatRules(rules);
})
}
ConvertFunction:
function convertRules(rules,sheetnames){
const output = [];
const ss = SpreadsheetApp.getActiveSpreadsheet();
sheetnames.forEach(sh => {
rules.forEach(rule => {
const copy = rule.copy();
const newRanges = [];
const oldRanges = copy.getRanges();
oldRanges.forEach(range => {
const buildRange = ss.getSheetByName(sh).getRange(range.getA1Notation());
newRanges.push(buildRange);
});
copy.setRanges(newRanges);
output.push(copy);
});
});
return output;
}
When you used ConditionalFormatRuleBuilder.copy(), it will return a ConditionalFormatRuleBuilder type data.
You need to use ConditionalFormatRuleBuilder.build() to generate a ConditionalFormatRule based on your modified range which can be used to set sheet's conditional format rules using Sheet.setConditionalFormatRules()
Example Code: (Copy Conditional Formatting Rules from Sheet1 to Sheet2 and change its range by adding column offset of 2)
function myFunction() {
var sourceSh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var targetSh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var rules = sourceSh.getConditionalFormatRules();
var newRules = [];
rules.forEach((rule, index) => {
Logger.log("****rule****");
var ruleBuilder = rule.copy();
var ranges = ruleBuilder.getRanges();
ranges.forEach(range => {
Logger.log(range.getA1Notation());
})
//Select C1:C1000 as new range
var newRange = targetSh.getRange(1,index + 3,1000,1);
ruleBuilder.setRanges([newRange]);
Logger.log("****new range****");
var ranges = ruleBuilder.getRanges();
ranges.forEach(range => {
Logger.log(range.getA1Notation());
})
Logger.log("****build modified rule****");
var newRule = ruleBuilder.build();
newRules.push(newRule);
});
targetSh.setConditionalFormatRules(newRules);
}
OUTPUT:
Sheet1:
Sheet2:
Your Code:
function convertRules(rules,sheetnames){
const output = [];
const ss = SpreadsheetApp.getActiveSpreadsheet();
sheetnames.forEach(sh => {
rules.forEach(rule => {
const copy = rule.copy();
const newRanges = [];
const oldRanges = copy.getRanges();
oldRanges.forEach(range => {
const buildRange = ss.getSheetByName(sh).getRange(range.getA1Notation());
newRanges.push(buildRange);
});
copy.setRanges(newRanges);
copy.build(); // Build conditional format rules based on modified range
output.push(copy);
});
});
return output;
}
I could be wrong because I don't use format rules much. But it seems to me that rules is an array of individual rule items. And the convertRules() returns that array so:
Perhaps this code might be something like this:
targets.forEach((target,i) => {
const sheet = ss.getSheetByName(target);
target.clearConditionalFormatRules();
target.setConditionalFormatRules(rules[i]);
})
I'm not really sure about this so if I'm wrong then I'll be glad to delete the answer.