I'm kinda new to coding and I'm trying to learn javascript by using it with google apps. I've figured out a few things trying to do my current project, but I seem to have gotten stuck.
I am attempting to create a program through google sheets that works sort of like a budgeting app, where I import data from my bank, run the program and it searches the data, changes the descriptions based on the content and adds a categorization.
I have figured out a good method for find and replace, but the problem I'm running into is that it only replaces part of the string instead of the whole thing. what i would like for it to do is search the data, if it finds a word that matches one of the cases i have entered, it will delete the entire string and replace it with a new description that I designate.
Here's what i have so far
function replaceStrings(){
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("D1:D" + sheet.getLastRow());
var data = range.getValues();
for (var i = 0; i<data.length; i++) {
if (data[i][1] != "") {
switch (data[i][1]) {
case data.indexOf('MCDONALD\'S') >-1 :
data[i][1].replace(data[i][1],'McDonalds');
break;
}
}
}
range.setValues(data)
}
this is based off of a find and replace function that i had worked out, not sure if I can include the "data[i][0]" in a replace function like that but I have also tried deleting the string and appending the new text but that didn't work either.
When I run this I don't get any error messages, but nothing happens.
I don't know what your data looks so I used some of my own but this technique seems to work fine for me. I used getDataRange and my own indexes but you can modify whatever you need. Or if you wish you can share your spreadsheet and I can customize to your exact requirements.
function replaceStrings()
{
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange()
var data = range.getValues();
for (var i = 0; i<data.length; i++)
{
if(data[i][1].match(/firstMatch/))
{
data[i][1]='replacement1';
}
if(data[i][1].match(/secondMatch/))
{
data[i][1]='replacement2';
}
}
range.setValues(data);
}
Your can use anything you want for the match strings. I happen to use regular expressions and if you find a match you can replace the entire data element with anything you wish.
Related
I have a Google Sheet with .gs script that is successfully generating dynamicnewRichTextValue() parameters which are meant to be injected into a Sheet cell that will contain multiple lines of text each with their own URL. I do not know all of the parameters in advance (might be one text and one link, or two each, or more) which is why I am dynamically generating the parameters.
Let's say the end-state should be this (in this case there are only two line items, but there could be more or less:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
In my script I don't know how many "setText" parameters or "setLinkUrl" statements I will need to generate, so I am doing it dynamically.
This is simple to handle for "setText" because I can just pass a single variable constructed during an earlier loop that builds the "setText" parameters. Let's call that variable setTextContent, and it works like this:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText(setTextContent)
So up to this point, everything is great. The problem is that I have another variable that generates the URL portion of the newrichtextvalue() parameters up to the ".build();" statement. So let's call that variable setUrlContent and it is built in an earlier loop and contains the string for the rest of the statement:
.setLinkURL(0,22,"mailto:fred#abcdef.com").setLinkURL(23,44,"mailto:jim#abcdef.com")
I am stumped trying to figure out how to attach it to the earlier bit. I feel like this is something simple I am forgetting. But I can't find it after much research. How do I hook up setUrlContent to the code above so that the command executes? I want to attach the bits above and get back to assigning it all to a variable I can put into a cell:
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com") // I can dynamically create up to here
.setLinkUrl(0,6,"mailto:fred#abcdef.com") // ...but these last couple lines are
.setLinkUrl(7,19,"mailto:jim#abcdef.com") // stuck in a string variable.
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
Thanks!
I believe your goal and situation as follows.
You want to use your script by dynamically changing the number of emails.
Modification points:
When your following script is run, I think that the links are reflected to mailto and fred#abcdef..
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
I thought that you might have wanted the linked email addresses like below.
fred#abcdef.com has the link of mailto:fred#abcdef.com.
jim#abcdef.com has the link of mailto:jim#abcdef.com.
In this answer, I would like to propose the modified script for above direction.
Modified script:
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => {
var v = e.trim();
return [v.split(":")[1], v];
});
var text = ar.map(([e]) => e).join(",");
var emailCell = SpreadsheetApp.newRichTextValue().setText(text);
var start = 0;
ar.forEach(([t, u], i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, u);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
In this modification, inputText is splitted to the hyperlink and the text (for example, when your sample value is used, it's fred#abcdef.com and mailto:fred#abcdef.com.), and the text including the hyperlink are put to the cell.
In this case, for example, even when var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com" is modified to var inputText = "mailto:fred#abcdef.com" and var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com,mailto:sample#abcdef.com", each hyperlink are reflected to each text.
Note:
When you want to the hyperlink of mailto:fred#abcdef.com to the text of mailto:fred#abcdef.com, you can also use the following modified script.
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => e.trim());
var emailCell = SpreadsheetApp.newRichTextValue().setText(inputText);
var start = 0;
ar.forEach((t, i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, t);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
References:
newRichTextValue()
Class RichTextValueBuilder
Class RichTextValue
I'm trying to execute a mailmerge script that reads fields from a spreadsheet and the template from a google doc. The script runs on the google doc.
The script works fine except it doesn't copy the formatting or table that is in the document. This is crucial.
Below is the code I am using.
function mmerge() {
var mdoc=DocumentApp.getActiveDocument();
var mdat=SpreadsheetApp.openById("1Nc2xu--").getSheetByName("MailMerge List);
var numcols=mdat.getDataRange().getNumColumns();
var numrows=mdat.getDataRange().getNumRows();
var mtxt=mdoc.getBody().getText();
var flds=[];
for (var j=0;j<numcols;j++) {flds.push(mdat.getRange(1, j+1).getValue());}
var rtxt;
for (var i=2; i<=numrows; i++) {
rtxt=mtxt
for (var j=1; j<=numcols; j++) {
rtxt=rtxt.replace("{"+flds[j-1]+"}",mdat.getRange(i, j).getValue());
}
mdoc.getBody().appendParagraph(rtxt);
}
}
I suspect that it has to do with the method used here: mtxt=mdoc.getBody().getText() but I don't know how to change it. My attempts to change it to copy() results in an error.
Does anyone know what I am doing wrong? Any help would be greatly appreciated.
Update: This is not a straight forward copy problem: the process involves having to execute the mail merge and then apply the formatting to the mailmerged doc.
Thanks
getText() returns a String, which has no character formatting.
The greater issue is that G Suite Services in Google Apps Script does not provide the document as HTML. (The getAs() method on the Document object only allows you to retrieve it as a PDF.)
You can adapt the "Google Doc to clean HTML converter" script, although it won't be trivial. It currently does not convert table content into HTML tables, but that would be a great contribution to the script that you could make! The script is written in a very straight-forward way, so should be quite simple to modify.
Specifically, you'll need to check for ElementTypes related to tables (TABLE, TABLE_ROW, TABLE_CELL) and then wrap those elements in the appropriate HTML tags. You can see how oazabir did it in this example:
if (item.getType() == DocumentApp.ElementType.PARAGRAPH) {
switch (item.getHeading()) {
// Add a # for each heading level. No break, so we accumulate the right number.
case DocumentApp.ParagraphHeading.HEADING6:
prefix = "<h6>", suffix = "</h6>"; break;
case DocumentApp.ParagraphHeading.HEADING5:
prefix = "<h5>", suffix = "</h5>"; break;
case DocumentApp.ParagraphHeading.HEADING4:
prefix = "<h4>", suffix = "</h4>"; break;
case DocumentApp.ParagraphHeading.HEADING3:
prefix = "<h3>", suffix = "</h3>"; break;
case DocumentApp.ParagraphHeading.HEADING2:
prefix = "<h2>", suffix = "</h2>"; break;
case DocumentApp.ParagraphHeading.HEADING1:
prefix = "<h1>", suffix = "</h1>"; break;
default:
prefix = "<p>", suffix = "</p>";
}
if (item.getNumChildren() == 0)
return "";
}
Once you've converted your template into HTML, then you can manipulate it using your existing code. (For example, have ConvertGoogleDocToCleanHtml() return the HTML text and save that into your mtxt variable.)
Lastly, in your current script, you make multiple calls to get the same range. These calls are really slow. Instead, try to get the range once, get the values, and then operate on the returned Array.
var mailMergeRange = SpreadsheetApp.openById("1Nc2xu--").getSheetByName("MailMerge List").getDataRange().getValues();
var numcols = mailMergeRange.length;
var numrows = mailMergeRange[0].length;
You could also try this approach using Advanced Drive Services and the MailChimp API.
I'm trying to create a Google sheets script which I hear is complete Javascript so I'm told. I'm just trying to create a list of quests relevant to an item in an online game by parsing the HTML in the spreadsheet.
Example: http://everquest.allakhazam.com/db/item.html?item=14295
Ideally in this case it should bring across the 4 quest names, their location and the quest ID (which can be found within the URL in the source code). But I'm just trying to pull the quest ID at the moment as can be found below.
function myFunction(itemid) {
var regexp = /quest.html\?quest\=(.*)"/;
var page = UrlFetchApp.fetch('http://everquest.allakhazam.com/db/item.html?item=' + itemid).getContentText();
var matches = [];
var number = page.match(/<li><a href="\/db\/quest.html\?quest\=(.*)"/);
for (var i in number) {
matches.push(number[i]);
}
return matches;
}
But the script just seems to hang on 'Loading..' and doesn't do anything. If I add 'return number just after the page.match it returns the first quest ID fine.. so it seems it may be related with pushing to the array which is causing the issues.
It is better not to parse HTML as text. You can use a formula with importxml function to get your data using XPath:
=importxml("http://everquest.allakhazam.com/db/item.html?item=14295";"//div[#id='quest']//b|//div[#id='quest']/ul/li/a|//div[#id='quest']/ul/li/a/#href")
The context: I need to process/correct many text documents containing multiple particular textual errors, highlight keywords in 'bold' and then output the result. I have a Google spreadsheet with two worksheets: one with two columns of 'wrong wordforms' and 'replacement wordforms' (2d array) that I intend to add to over time and use it as a datastore to 'call from;' the other, a single-column collection of words (1d array) I designate "keywords" to check for and then highlight in the target documents.
Things I've tried that worked: I used a basic array iteration loop from a beginner video (I can't add more links yet, I apologize) and swapped in body.replaceText() for the sendEmail(), successfully, to process the corrections from my "datastore" into my target document, which works nearly perfectly. It ignores text values without the exact same case...but that's a problem for another day.
function fixWords() {
// Document to edit
var td = DocumentApp.openById('docId1');
// Document holding comparison datastore
var ss = SpreadsheetApp.openById('docId2');
// Create data objects
var body = td.getBody();
var sheet = ss.getSheetByName("Word Replacements");
var range = sheet.getDataRange();
var values = range.getValues();
// Create a loop (iterate through the cell data)
for (i=1;i<values.length;i++) {
fault = values[i][0];
solution = values[i][2];
body.replaceText(fault, solution);
}
}
Things I've tried that fail: I then tried just swapping out values for setBold() with the replaceText() code, but the closest I got was the first instance of a keyword from the array would be styled correctly, but no further instances of it...unlike ALL of the instances of an incorrectly spelled word being corrected from the Word Replacements array using the fixWords function.
I found the 'highlightTextTwo' example here at stackoverflow which works very well, but I couldn't figure out how to swap in an external data source or force the included different iteration loop to work in my favor.
I've scanned the GAS reference, watched Google developer videos for snippets that might apply...but clearly I'm missing something that's probably basic to programming. But I honestly don't know why this isn't as easy as the body.replaceText() functionality.
function boldKeywords() { // https://stackoverflow.com/questions/12064972
// Document to edit
var doc = DocumentApp.openById('docId1');
// Access the keyword worksheet, create objects
var ss = SpreadsheetApp.openById('docId2');
var sheet = ss.getSheetByName("Keywords");
var range = sheet.getDataRange();
var values = range.getValues();
var highlightStyle = {};
highlightStyle[DocumentApp.Attribute.BOLD] = 'true';
for (i=1; i<values.length; ++i) {
textLocation = values[i];
if (textLocation != null && textLocation.getStartOffset() != -1) {
textLocation.getElement().setAttributes(textLocation.getStartOffset(),textLocation.getEndOffsetInclusive(), highlightStyle);
}
}
}
This throws out 'TypeError: Cannot find function getStartOffset in object DIV. (line 15, file "boldIt").' I guess this means that by just blindly swapping in this code, it's looking in the wrong object...but I cannot figure out why it works for x.replaceText() and not for x.setAttributes() or x.setBold or .getElement().getText().editAsText()...there just doesn't seem to be a "Learn Google Apps Script example" that deals with something this low on a scale of mundane, uninteresting use cases...enough for me to figure out how to direct it to the right object, and then manipulate the "if statement" parameters to get the behavior I need.
My current brick wall: I spotted this example, again, Text formatting for strings in Google Documents from Google Apps Script, and it seemed promising, even though the DocsList syntax has been deprecated (I'm fairly sure). But now I get "bold is not defined" thrown at me. Bold...is not defined. :: mouth agape ::
function boldKeywords() {
// Access the keyword worksheet, create objects
var ss = SpreadsheetApp.openById('docId1');
var sheet = ss.getSheetByName("Keyterms");
var range = sheet.getDataRange();
var values = range.getValues();
// Open target document for editing
var doc = DocumentApp.openById('docId2');
var body = doc.getBody();
// Loop function: find given keyword value from spreadsheet in target document
// and then bold it (highlight with style 'bold')
for (i=1; i<values.length; ++i) {
keyword = values[i];
target = body.findText(keyword);
body.replaceText(target,keyword);
text = body.editAsText();
text.setBold(text.startOffset, text.endOffsetInclusive, bold);
}
}
I will happily sacrifice my firstborn so that your crops may flourish for the coming year in exchange for some insight.
I use this for my scripts, the setStyleAttribute method.
Documentation : https://developers.google.com/apps-script/ui_supportedStyles
Example :
TexBox.setStyleAttribute("fontWeight", "bold");
The bold parameter is a Boolean data type. You need to use the word true or false.
Replace "bold" with "true".
text.setBold(text.startOffset, text.endOffsetInclusive, true);
Check out the "Type" column in the documentation:
Google Documentation - setBold
I'm new to this site for the main purpose that I plan to pursue a career in programming. I've landed my first job at an engineering company who is asking me to set up a system in which they can easily determine the time between a job being filed, and it's completion. We're using spreadsheet docs right now to accomplish certain pieces of this.
I'm looking to create a custom function in Google Docs that will allow me to traverse the array of values in row C and then compare it with a number that the function was called with, compare the number to the number in the array and give me which one is the smaller number. EDIT: The function will be called on another sheet called "parsed data" located in the same project file. It's purpose is to automatically file the order number of a current project (just for the sake of being organized) All the other functions I plan to implement will be based off of this order number being correct.
So far, I've gathered this much (I'm learning this on the fly because I still lack experience, so bear with me.)
{
/**created by Alexander Bickford for use at Double E Company
*sorts through a range of values to determine the lowest next value
*returns lowest determined value of next cell
*/
//List Of To Be Implemented Functions
// sheet.appendRow
function setValue(num)
{
var ss = SpreadsheetApp.getActiveSpreadsheet('parsed data');
var ss = ss.getSheets()[0];
var myRange = ss.getRange("C:C").getValues();
newValues = [];
for(i=1;i<=myRange;i++) //Loop to traverse the C range and find the lowest value.
{
if(num<=range[3][i])
{
}
else
num = range[3][i];
}
return num;
}
}
when I call the function in the spreadsheet, I'm getting an error passed that says:
error: ReferenceError: "SPREADSHEET_ID_GOES_HERE" is not defined. (line 8, file "Code")
Google predefines some functions at the top that look like this:
/**
* Retrieves all the rows in the active spreadsheet that contain data and logs the
* values for each row.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
function readRows() { <---Line 8 in the file
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
Logger.log(row);
}
};
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the readRows() function specified above.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Read Data",
functionName : "readRows"
}];
sheet.addMenu("Script Center Menu", entries);
};
End Code I don't need */
I assume it has something to do with the earlier lines (I pointed out line 8). Any thoughts?
Below code is working fine for me.
var ss = SpreadsheetApp.getActiveSheet();
var myRange = ss.getRange("C:C").getValues();
newValues = [];
for(i=1;i<=myRange.length;i++)
{
Logger.log(myRange[i]);
}
Looking at your code, it seems like you have a few problems.
You seem to be mixing up "sheets" with "spreadsheet", and your redundant declaration of "ss" as a variable is bound to cause you some problems.
You seem to be passing in arguments to the incorrect methods. I had this same problem when working with the Google App script earlier. It took a lot of poking around Google's Documentation (which you should really take a look at: https://developers.google.com/apps-script/). You seem to be making the same mistake I did, coding by analogy. looking at Google's sample code and trying to replicate is bound to bump you into some trouble.
Some useful advice:
The most confusing thing to wrap your head around is the structure: spreadsheet>>sheet>>range, you have to explicitely deal with the one's on top before moving to the one's on the bottom.
Remove the 'parsed data' argument from getActiveSpreadsheet(), it should be blank. What you want to use is "getSheetByName("parsed data")" and pass that into a sheet variable.
In your for loop, you also need to use the ".length" method, or use the ".getLastRow()" method with a sheet object to find the last row in your sheet.
Your code might look something like this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("parsed data");
var endRowNumber = sheet1.getLastRow();
//insert rest of code