`Hi !
I'm trying to replace text in all cells in all table of a google slide.
I successed with shapes, but not with tables and cells..
And when i loop in using forEach. I only can reach the two first cells of my table.
If i try to reach all with a for loop or one by one (with getCell(1.0), getCell(2.0) etc...), the function doesn't work anymore.
Here my current code
var slides = deck.getSlides();
slides.forEach(function(slide){
var tables = (slide.getTables());
tables.forEach(function(table){
table.getCell(0,0).getText().replaceAllText('{{entreprise}}',entreprise);
table.getCell(1,0).getText().replaceAllText('{{entreprise}}',entreprise);
})
})
Any help with this will be very much appreciated!
the goal is to automated my google slides and replace some variables. with a google form.
Thanks by advance`
I believe your goal in your question is as follows.
You want to resolve your current issue of I'm trying to replace text in all cells in all table of a google slide. I successed with shapes, but not with tables and cells...
You want to replace texts in tables of all slides in the Google Slides using Google Apps Script.
In this case, how about the following modification?
Modified script:
function myFunction() {
// Please set your value. You can set multiple patterns.
var replaceObj = [
{ "from": "{{entreprise}}", "to": "sample1" },
,
,
];
var deck = SlidesApp.getActivePresentation();
var slides = deck.getSlides();
slides.forEach(function (slide) {
var tables = slide.getTables();
tables.forEach(function (table) {
for (var r = 0; r < table.getNumRows(); r++) {
for (var c = 0; c < table.getRow(r).getNumCells(); c++) {
replaceObj.forEach(({ from, to }) => table.getCell(r, c).getText().replaceAllText(from, to));
}
}
})
})
}
By this modification, all tables of all slides in Google Slides are used, and the texts in tables are replaced using replaceObj.
In this modification, you can set multiple replace patterns to replaceObj.
References:
getNumRows()
getNumCells()
Related
With Google Sheets I created a calculation table which I want to copy to a quote template in Google Docs with Google Apps Script. For this I use the solution found here in this question: Using Google Apps Script to Copy from Google Sheets Data Table to Google Documents Table.
I use (literally exactly) the first sample script, without taking care of column width, as this is not an issue for me.
The copying works great, but I still have a few problems:
all the cell values are copied as text, so in the Google Docs table I have to reformat some values in Euro
in the Google Docs table all the cells are left aligned, I need to right align the columns with Euro values
the script uses "body.appendTable(values)" to insert table data at the end of the document, but how can I determine where in the document I want to place the table?
once created the new document from the template I would like to open it directly in the browser
My script is bound to the Google Sheet document, where I start the script from a custom menu.
You can see the calculation sheet here and the template here.
Hope to find someone who can help.
EDIT to answer questions from comments:
Hey J.G., maybe the problem wasn't explained well enough or I am not smart enough to handle the solutions you proposed:
setNumberFormat() worked for me only on a spreadsheet, but I have to format the table in Google Docs document.
With the insertTable(childIndex, table) I was able to insert the table only at the beginning or the end of the document. What am I missing?
OK, it places the table on line 5 in this case, tried also many other indexes and was able to come to the position I want it to be. But this is not reliable enough for me, because if the template changes (and it will change!) I have to find the right index again and change the script. Or is there a possibility to search for a keyword in the doc and get it's childIndex via script?
Issues 1-3 can be easily solved. Here is the working script:
Code:
function myFunction() {
// Get Google Sheet data
var ssId = 'sample sheet id';
var docId = 'sample doc id';
var ss = SpreadsheetApp.openById(ssId); // Please set the Spreadsheet ID.
var sheet = ss.getSheetByName("DataFilter");
var range = sheet.getRange(4, 1, 1, 5).getDataRegion(SpreadsheetApp.Dimension.ROWS);
// to include euro signs
var values = range.getDisplayValues();
var backgroundColors = range.getBackgrounds();
var styles = range.getTextStyles();
// Position to paste data in Google Docs
var body = DocumentApp.openById(docId).getBody();
var numElements = body.getNumChildren();
// if 'Table_goes_here' is not found, append to bottom
var index = numElements;
// var index = body.getChildIndex(body.findText('Table_goes_here').getElement().getParent());
// index wasn't found using the code above for some reason, so doing loop instead
for (var i = 0; i < numElements; i++) {
var child = body.getChild(i);
if (child.asText().getText() == 'Table_goes_here') {
index = i;
// remove child
body.removeChild(child);
break;
}
}
// then insert your table
var table = body.insertTable(index, values);
table.setBorderWidth(0);
for (var i = 0; i < table.getNumRows(); i++) {
for (var j = 0; j < table.getRow(i).getNumCells(); j++) {
var obj = {};
obj[DocumentApp.Attribute.BACKGROUND_COLOR] = backgroundColors[i][j];
obj[DocumentApp.Attribute.FONT_SIZE] = styles[i][j].getFontSize();
if (styles[i][j].isBold()) {
obj[DocumentApp.Attribute.BOLD] = true;
}
// if euro sign is found in text, align right
if (table.getRow(i).getCell(j).getText().includes('€')) {
table.getRow(i).getCell(j).getChild(0).asParagraph().setAlignment(DocumentApp.HorizontalAlignment.RIGHT);
}
table.getRow(i).getCell(j).setAttributes(obj);
}
}
}
start of table:
end of table:
Note:
Issue 4 is a tricky one. Apps Script cannot easily communicate with the browser (afaik) since this runs on the server side, not client. You can try showing the document on a WebApp instead.
Resources:
https://developers.google.com/apps-script/reference/document/horizontal-alignment
https://developers.google.com/apps-script/reference/document/body#inserttablechildindex,-cells
I am working on a slide that would take dynamic data from Google sheet.
I want to keep the slide template
Update the slide values in real-time
I want to use Google app scripts to connect the data from the sheet to the slide. I am new to Java Scripts and following a tutorial online to achieve this but with no luck.
My 2 files are set up as follows
and the slides I am working on can be found here
A copy of the data can also be found here
The tutorial I am following is found in this link
Below is the code I have implemented but the problem is it does not work and tries to create multiple pages and repeat same value on each page. I only want one page with the values updated from the sheet
Your help would be grately appareciated. Thank you
function generateLandingPagesReport() {
var dataSpreadsheetUrl = "https://docs.google.com/spreadsheets/d/1UccsFtqKKsXhlus4-02dXfJP0ECcCsBUmLNZajJnS_4/edit"; //make sure this includes the '/edit at the end
var ss = SpreadsheetApp.openByUrl(dataSpreadsheetUrl);
var deck = SlidesApp.getActivePresentation();
var sheet = ss.getSheetByName('metrics'); // sheet name
var values = sheet.getRange('C4:L12').getValues(); // range for values
var slides = deck.getSlides();
var templateSlide = slides[0];
var presLength = slides.length;
values.forEach(function(page){
if(page[0]){
var Current = page[2];
var Target = page[3];
var Emea = page[5];
var Depac = page[7];
var Emma = page[9];
var Comment = page[11];
templateSlide.duplicate(); //duplicate the template page
slides = deck.getSlides(); //update the slides array for indexes and length
newSlide = slides[2]; // declare the new page to update
var shapes = (newSlide.getShapes());
shapes.forEach(function(shape){
shape.getText().replaceAllText('{{Current}}',Current);
shape.getText().replaceAllText('{{Target}}',Target);
shape.getText().replaceAllText('{{Emea}}',Emea);
shape.getText().replaceAllText('{{Depac}}',Depac)
shape.getText().replaceAllText('{{Emma}}',Emma);
shape.getText().replaceAllText('{{Comment}}',Comment);
});
presLength = slides.length;
newSlide.move(presLength);
} // end our conditional statement
}); //close our loop of values
//Remove the template slide
templateSlide.remove();
}
I believe your goal as follows.
You want to convert the table from Google Spreadsheet to Google Slides.
As the sample situation, you want to achieve as follows. This is from your question.
When I saw your sample situation, I can confirm that there are 2 values of Samuel Tuffuor in Google Slides. From this situation, you want to put the values to the Google Slides using the row titles of METRIC.
Modification points:
In your current script,
duplicate() is used in the loop. By this, new slide is inserted every loop. I think that this is one of several reasons of your issue.
At 1st loop of values.forEach(), all values are replaced. Because in the template of your Google Slides, {{current}}, {{Target}}, {{Emea}}, {{Depac}}, {{Emma}}and{{Comment}}of each row is replaced by eachreplaceAllText`.
In this case, it is required to distinguish {{current}}, {{Target}}, {{Emea}}, {{Depac}}, {{Emma}}and{{Comment}}` of each row.
In order to distinguish the values of each row of template of Google Slides, I would like to propose to group each row.
Usage:
1. Grouping each row of template of Google Slides.
Please group each row of template of Google Slides as follows. The red dotted line is the groups. In your sample Google Slides, 9 groups are created. Using these groups, the values retrieved from Google Spreadsheet are replaced with the placeholders of each group.
2. Sample script.
In order to replace the values retrieved from Google Spreadsheet with the place holders of each row of Google Slides, I modified your script as follows.
function generateLandingPagesReport() {
var dataSpreadsheetUrl = "https://docs.google.com/spreadsheets/d/1UccsFtqKKsXhlus4-02dXfJP0ECcCsBUmLNZajJnS_4/edit";
var ss = SpreadsheetApp.openByUrl(dataSpreadsheetUrl);
var deck = SlidesApp.getActivePresentation();
// 1. Retrieve values from Google Spreadsheet.
var sheet = ss.getSheetByName('metrics');
var values = sheet.getRange('A4:L12').getDisplayValues(); // or .getValues();
// 2. Create an object for using replacing the values.
var obj = values.reduce((o, [a,,c,d,,f,,h,,j,,l]) => Object.assign(o, {[a.trim()]: {"{{current}}": c, "{{Target}}": d, "{{Emea}}": f, "{{Depac}}": h, "{{Emma}}": j, "{{Comment}}": l}}), {});
// 3. Replace values for each group.
var slides = deck.getSlides();
var templateSlide = slides[0];
var groups = templateSlide.getGroups();
groups.forEach(g => {
var c = g.getChildren();
var key = "";
var r = new RegExp(/{{\w.+}}/);
for (var i = 0; i < c.length; i++) {
var t = c[i].asShape().getText().asString().trim();
if (!r.test(t)) {
key = t;
break;
}
}
// I modified below script as the additional modification.
if (obj[key]) {
c.forEach(h => {
var t = h.asShape().getText().asString().trim();
if (r.test(t)) h.asShape().getText().setText(obj[key][t]);
});
}
});
}
The flow of this modified script is as follows.
Retrieve values from Google Spreadsheet.
Create an object for using replacing the values.
Replace values for each group.
3. Result.
When the modified script is used for your sample Google Spreadsheet and the sample Google Slides with grouping each row, the following result is obtained. In this case, the 1st slide in the Google Slides is used. So templateSlide.remove(); is not required to be used.
Note:
In your script, the values are retrieved from Google Spreadsheet using getValues(). In this case, 10 % is 0.1. If you want to use 10 %, please use getDisplayValues() instead of getValues(). In above modified script, getDisplayValues() is used.
References:
replaceAllText
setText(newText)
getDisplayValues()
Added:
The reason of your current issue is due to the different row titles between Google Slides and Google Spreadsheet. There are 2 different titles in your sample Google Slides and Google Spreadsheet.
Mashal Mashal and Mashal Mashal for Google Slides and Google Spreadsheet, respectively.
Mashal Mashal is 1 space between Mashal and Mashal.
Mashal Mashal is 2 spaces between Mashal and Mashal.
Chelsea Great and Chelea Great for Google Slides and Google Spreadsheet, respectively.
Please use the same row titles for Google Slides and Google Spreadsheet. When the row title is different, I modified above script for not replacing the values. So could you please confirm it? When above modified script is used for your Google Slides and Google Spreadsheet, the rows of Mashal Mashal and Chelsea Great are not replaced.
Is there a way to write a Google Apps Script in Google Docs to retrieve from Google Sheets a range limited to non-blank rows and display those rows as a table?
I'm looking for a script to copy non-blank rows of data from a Google Sheets range of cells to a table in Google Documents using Google Apps Script (which I've limited experience with).
The data to be copied seem too large for linking directly to Google Documents so the Copy-Paste action from spreadsheet to document does not prompt a choice for linking the data.
Also the number of rows is dynamic so a fixed range wouldn't resolve the problem. In the spreadsheet, I've used the SORTN function and set it to display ties so the size of the non-blank rows of the range changes.
I've started with the following code outline:
function myFunction() {
// Get Google Sheet data
var app = SpreadsheetApp;
var ss = app.openById('SHEET_ID');
var activeSheet = app.getActiveSpreadsheet();
var range = activeSheet.getRange("B4:D");
// Position to paste data in Google Docs
var doc = DocumentApp.getActiveDocument();
var body = DocumentApp.getActiveDocument().getBody();
// Build a table from the array.
body.appendTable(cells);
}
This is closest question found on SE but doesn't answer this query: Copying Sheet Data to Doc table.
You want to copy the data range from Google Spreadsheet to Google Document as the table.
You want to append the table to the Google Document.
In this case, you don't want to include the empty rows of the bottom of sheet to the values.
In your situation, you are not required to link the original Spreadsheet to the table of Document.
You want to achieve this using Google Apps Script.
I could understand like above. If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Flow:
The flow of this sample script is as follows.
Retrieve the data range from Google Spreadsheet.
In this case, I used getDataRegion(dimension).
Retrieve values, backgrounds and text styles using the range.
Append new table to Google Document with the values.
Set the format of cells.
Sample script:
From your script in your question, it supposes that the script is the container-bound script of Google Document. So in order to test the script, please put the following script to the container-bound script of Google Document you shared.
function myFunction() {
// Get Google Sheet data
var ss = SpreadsheetApp.openById("###"); // Please set the Spreadsheet ID.
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getRange(4, 2, 1, 5).getDataRegion(SpreadsheetApp.Dimension.ROWS);
var values = range.getValues();
var backgroundColors = range.getBackgrounds();
var styles = range.getTextStyles();
// Position to paste data in Google Docs
var body = DocumentApp.getActiveDocument().getBody();
var table = body.appendTable(values);
table.setBorderWidth(0);
for (var i = 0; i < table.getNumRows(); i++) {
for (var j = 0; j < table.getRow(i).getNumCells(); j++) {
var obj = {};
obj[DocumentApp.Attribute.BACKGROUND_COLOR] = backgroundColors[i][j];
obj[DocumentApp.Attribute.FONT_SIZE] = styles[i][j].getFontSize();
if (styles[i][j].isBold()) {
obj[DocumentApp.Attribute.BOLD] = true;
}
table.getRow(i).getCell(j).setAttributes(obj);
}
}
}
Note:
In this sample script, the values of cells "B4:F" are retrieved from your shared Spreadsheet. So if you want to change this range, please modify above script.
References:
getDataRegion(dimension)
getValues()
getBackgrounds()
getTextStyles()
appendTable()
setAttributes(attributes)
Added:
Issue for reflecting column width from Spreadsheet to Document:
It is possible to reflect the column width. But when the column width is set to Google Document, it seems that the result is different from that of the direct copy&paste the table, even when the unit is converted from Spreadsheet to Document.
In your shared Spreadsheet, the widths of column "B" to "F" are 21, 100, 100, 100 and 100 pixels, respectively. But it was found that when the table is manually copied from Spreadsheet to Document, each column width is changed from the original size. By this, unfortunately, when the column width of table is copied by the script, the result by manual copy cannot be replicated.
Sample script:
At the following sample script, the column width of Google Spreadsheet is copied to the table of Google Document.
function myFunction() {
// Get Google Sheet data
var ss = SpreadsheetApp.openById("###"); // Please set the Spreadsheet ID.
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getRange(4, 2, 1, 5).getDataRegion(SpreadsheetApp.Dimension.ROWS);
var values = range.getValues();
var backgroundColors = range.getBackgrounds();
var styles = range.getTextStyles();
var colWidth = []; // Added
for (var col = 2; col <= 6; col++) { // Added
colWidth.push(sheet.getColumnWidth(col) * 3 / 4);
}
// Position to paste data in Google Docs
var body = DocumentApp.getActiveDocument().getBody();
var table = body.appendTable(values);
table.setBorderWidth(0);
colWidth.forEach(function(e, i) {table.setColumnWidth(i, e)}); // Added
for (var i = 0; i < table.getNumRows(); i++) {
for (var j = 0; j < table.getRow(i).getNumCells(); j++) {
var obj = {};
obj[DocumentApp.Attribute.BACKGROUND_COLOR] = backgroundColors[i][j];
obj[DocumentApp.Attribute.FONT_SIZE] = styles[i][j].getFontSize();
if (styles[i][j].isBold()) {
obj[DocumentApp.Attribute.BOLD] = true;
}
table.getRow(i).getCell(j).setAttributes(obj);
}
}
}
When you run the script, you can see the created table is different from the manually copied table. So about the width of column, in the current stage, please give the values for manually setting colWidth. Or please adjust the column width of Document side by modifying the column width of Spreadsheet side. Or please use above script. This is due to my poor skill. I deeply apologize for this.
(On Google Sheets)
I want to create a script that runs only on certain sheets in a spreadsheet. To do this I have created a script that creates an array of sheet names whose names (defined as a variable curSheetName) fit the following condition:
if(curSheetName == "CM:")
so currently the sheet name must equal "CM:", however I want it to be so that the script runs on all sheets that CONTAIN the text "CM:" in their title, but I can't find how to write that syntactically. In MySQL it is '%CM:%', in GSheet formulas you just use asterisks..
Here is my code:
// Get list of sheet names in an array format
function sheetnames() {
// Define array
var out = new Array()
// Get sheets
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
// Work through each sheet one at a time
for (var i=0 ; i<sheets.length ; i++) {
// define the current sheet
var curSheetName = sheets[i].getName();
// determine if matches criteria
if(curSheetName == "CM:") { //***************** NEED HELP HERE
// put into array if matches criteria
out.push( [ sheets[i].getName() ] );
// RUN FUNCTION HERE
}
}
}
Any help appreciated!
Cheers,
Mike
You can use the JavaScript Array.indexOf() method to achieve this.
Example:
if (curSheetName.indexOf('CM:') !== -1) {
...
}
Documentation:
JavaScript Array.indexOf().
Is it possible to get the filter criteria of my data set. For example, if one of my column is "Department" and I filtered the data to display only "IT". How do we get the filtered criteria, "IT". I need to get that filtered criteria into my GAS to do some other manipulation.
Thanks.
Try this example for a simple filter. It will filter information (change XXX to something meaningful) from the first column:
function onOpen(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var filterMenuEntries = [{name: "filter Column1", functionName: "filter"}];
ss.addMenu("choose filter", filterMenuEntries);
}
function filter(){
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i=1; i <=numRows -1; i++){
var row =values[i];
// Column value
var myValue = row[0];
// filter value
if (myValue == "XXX"){
sheet.hideRows(i+1);
}
}
}
Google Spreadsheet already has a FILTER formula (I always use this page to remind me how to do it). So for example if your data looked like this
A
1 Department
2 IT Department (Edinburgh)
3 Department of IT
4 Other Department
to get a filtered list you could use the formula
=FILTER(A:A;FIND("IT",A:A)>0)
(Working example here)
If you want to do something entirely in Apps Script Romain Vialard has written a Managed Library with a filter function. Here are instructions for installing and using the 2D Array2 library
Filters are now available using with the recent launch of the Advanced Sheets Service:
Here is a link to the article
Here is a little snippet of how to do :
function setSheetBasicFilter(ssId, BasicFilterSettings) {
//requests is an array of batchrequests, here we only use setBasicFilter
var requests = [
{
"setBasicFilter": {
"filter": BasicFilterSettings
}
}
];
Sheets.Spreadsheets.batchUpdate({'requests': requests}, ssId);
}
In search for an answer to this question, I came up with the next workaround:
apply filters in the sheet;
color the filtered (and therefore
visible) cells (in the example code red in column F);
run script:
reading background colors (of column F) in array colors
iterating this array
building up the new array of row numbers that are visible.
This last array can be used to further manipulations of the sheet data.
To vivualize the effect of the script, I made the script setting the background of used cells to green.
It's one extra small effort for the end user to make, but IMHO it's the only way to make it possible to only use the filtered data.
function getFilterdData(){
var s = SpreadsheetApp.getActive();
var sheet= s.getSheetByName('Opdrachten en aanvragen');//any sheet
var rows = new Array();
var colors = sheet.getRange(1, 6, sheet.getLastRow(), 1).getBackgrounds();
for(var i = 0; i < colors.length; i++){
if(colors[i] == "#ff0000"){
var rowsIndex = rows.length;
rows[rowsIndex] = i+1;
sheet.getRange(i+1, 6).setBackground("#d9ead3")
}
}
}
Based on mhawksey answer, I wrote the following function:
//
function celdasOcultas(ssId, rangeA1) {
// limit what's returned from the API
var fields = "sheets(data(rowMetadata(hiddenByFilter)),properties/sheetId)";
var sheets = Sheets.Spreadsheets.get(ssId, {ranges: rangeA1, fields: fields}).sheets;
if (sheets) {
return sheets[0].data[0].rowMetadata;
}
}
then called it this way:
// i.e. : ss = spreadsheet, ranges = "Hoja 2!A2:A16"
Logger.log(range.getA1Notation());
var ocultas = celdasOcultas(ss.getId(), sheetName + "!" + range.getA1Notation());
// Logger.log(ocultas);
var values = range.getValues();
for (var r = 0; r < values.length; r++) {
if (!ocultas[r].hiddenByFilter) {
values[r].forEach( function col(c){
Logger.log(c);
});
}
//range.setBackground("lightcoral");
}
Than prevent the log of the hidden rows. You can see it in action at:
Prueba script, note project has to get Google Sheets API enabled on Google API Console.
Hope it helps. Thank you!
This as raised in Google's Issue Tracker as Issue #36753410.
As of 2018-04-12, they have posted a solution:
Yesterday we released some new functionality that makes it possible to
manipulate filters with Apps Script:
https://developers.google.com/apps-script/reference/spreadsheet/range#createFilter()
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getfilter
https://developers.google.com/apps-script/reference/spreadsheet/filter
Unfortunately Apps Script still doesn't have methods for determining
if a given row or column is hidden by the filter. See comment #157 for
a workaround utilizing the Sheets Advanced Service.
The Filter class lets you get the FilterCriteria for each column in turn.