I'm trying to extract numbers such as the ones listed below from my Gmail messages using Google Apps Script.
2,495.00
1,594
3,777.23
642.00
This is the code:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search('subject:(Transaction) after:2016/7/31 before:2016/8/10');
for (var i=0; i<threads.length; i++)
{
var messages = threads[i].getMessages();
var tmp;
for (var j=0; j<messages.length; j++)
{
var content = messages[j].getBody();
var subject = messages[j].getSubject();
var date = messages[j].getDate();
Logger.log(content);
if (content)
{
tmp = content.match(/\d+(,\d+)*(\.\d+(e\d+)?)?/);
var number = (tmp && tmp[j]) ? tmp[j] : 'No number';
sheet.appendRow([number, subject, date]);
}
else
{
sheet.appendRow([content, subject, date]);
}
}
}
}
I've been getting mixed results. For some messages this works as intended but for some it completely skips the numbers from the messages. I'm a newbie to JS/GAS and I thought the problem was in the regex but I'm not sure. Any help in this would be appreciated.
you are facing two trouble here:
the regex you are using don't look optimised (but neither what you are looking is clear a number like 1,594 should not be found if you are also looking at number that look like that 642.00). Nevertheless you could use a a regex like Shekhar Khairnar proposed in comment or something similar (the gat the end is important as there is more than one number in your mail).
The second trouble is in the line var number = (tmp && tmp[j]) ? tmp[j] : 'No number';. Why is there a jvar in this line? jis reference to the for loop --> number of messages, nothing to do with the occurences in your message.
What I can propose you is something like that:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search('test');
var re = /(?:\d{1,3}[,])*\d{1,3}\.{0,1}\d{1,3}/g;
for (var i=0; i<threads.length && i<5; i++) // added a condition because I didn't wanted to have too many results
{
var messages = threads[i].getMessages();
var tmp;
for (var j=0; j<messages.length; j++)
{
var content = messages[j].getPlainBody(); //.getBody();
var subject = messages[j].getSubject();
var date = messages[j].getDate();
//Logger.log(content);
if (content)
{
tmp = content.match(re); // /\d+(,\d+)*(\.\d+(e\d+)?)?/);
var number = tmp || ['No number']; // result of tmp is either null or an array --> if it's null then it will take the value 'no number'
Logger.log(number);
sheet.appendRow([number.join(" | "), subject, date]);
}
else
{
sheet.appendRow([content, subject, date]);
}
}
}
}
Related
I tried to write an Apps script for sending emails for those who forgot to update status report using google form.
It's my 1st script and I tried to fix it for almost 5 hours unsuccessfully.
Here is code:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("summary");
var lastRow = sheet.getLastRow();
for ( var i=2; i<=lastRow; i++){
var currentEmail = sheet.getRange(i,11).getValue();
var templateText = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("emailbody").getRange(1,1).getValue();
var emailRecipient = sheet.getRange(i,2).getValue();
var messageBody = templateText.replace("{name}",emailRecipient);
var oldCheck = sheet.getRange(i,12).getValue();
if (oldCheck = "Old"){
MailApp.sendEmail(currentEmail,"Reminder: Fill the weekly status update form",messageBody);
}
else {
continue;
}
}
}
It sends only 1 email for the first "old" check.
Explanation:
Issue:
You want to compare the value of oldCheck with "Old" and not assign "Old" to oldCheck. You need to use == instead of =.
Improvements:
It is computationally expensive to use getValue inside a for loop. Instead use getValues before the for loop and work with the arrays. In this way, you eliminate reduntant API calls that make your script heavy especially when i increases.
Solution:
function sendEmails() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName("summary");
var data = sheet.getDataRange().getValues();
var templateText = spreadsheet.getSheetByName("emailbody").getRange(1,1).getValue();
for (var i=1; i<data.length; i++){
var currentEmail = data[i][12];
var emailRecipient = data[i][3];
var messageBody = templateText.replace("{name}",emailRecipient);
var oldCheck = data[i][13];
// be careful! == instead of =
if (oldCheck == "Old"){
MailApp.sendEmail(currentEmail,"Reminder: Fill the weekly status update form",messageBody);
}
else {
continue;
}
}
}
I'm having trouble matching a string in an array. Column B2:Lastrow is defined as array which is "ID". I am trying to paste only unique entries to google sheet which aren't available in Column B2:Lastrow. Issue is..when I run the code it allows duplicates in the google sheet as well.
I was using it through count formula on the sheet but that leads to maximum code runtime error..hence I'm using the range as an array. Solves the error but not able to recognize if the string is unique.
// Code: List Gmail Label to Google Sheet and save attachment to GDrive
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName('Summary');
var label = GmailApp.getUserLabelByName("Caterpiller Account");
var threads = label.getThreads();
function getEmails() {
for (var i = 0; i < threads.length; i++) {
var row = sheet.getLastRow() + 1;
var message = threads[i].getMessages()[0];
var ID = message.getId();
var fulldata = sheet.getRange('B2:B' + row).getValues();
if (fulldata.indexOf(ID) == -1) {
var messages=threads[i].getMessages();
var listID=threads[i].getPermalink();
var listdate=threads[i].getLastMessageDate();
var message = threads[i].getMessages()[0];
var attachment = message.getAttachments();
var attachmentBlob = message.getAttachments()[0].copyBlob();
var folder = DriveApp.getFolderById("1ilsecZOexqTWGfAMu5xJDx1pKh3z1US-");
// EXTRACTOR CODE:
for (var m=0; m < messages.length; m++) {
sheet.getRange(row,1).setValue(messages[m].getSubject());
sheet.getRange(row,2).setValue(ID);
sheet.getRange(row,3).setValue(listdate); // Value - Date
for (var z=0; z<attachment.length; z++) {
var file = DriveApp.getFolderById("1ilsecZOexqTWGfAMu5xJDx1pKh3z1US-").createFile(attachmentBlob);
//Pending: Weblinkview (basically get permanent url of file) / Or self developed function that gets file through description (where description is email ID)
}
row++;
}
}
}
}
Expected: Unique entries & a faster Code runtime.
Actual: I'm crap & code time is still the same.
bool IsSame(string str,char arr[100])
{
if(str.lenght!=strlen(arr))return false;
for(int i=0;i<str.lenght;i++)
{
if(str[i]!=arr[i]) return false;
}
return true;
}
I am trying to create a google script that will scan through a specific column of one google sheet and check if any new additions are a duplicate. I have come up with the following, but it isn't working.
function hasDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Customer and form Details");
var array = sheet.getRange('B2:B' + lastRow).getValues();
var valuesSoFar = [];
for (var i = 0; i < array.length; ++i) {
var value = array[i];
if (value in valuesSoFar) {
SpreadsheetApp.getUi().alert('Hello, world!');
}
else{
valuesSoFar.push(value);
}
}
}
As far as Im aware the above code creates an array called array and populates it with the specific range I am interested in. I then create a new, empty, array called valuesSoFar. The code then loops through the column and sequentially checks if that item has already been seen before, if so it gives you an alert. If not it adds it to the list of new items and keeps going.
It looks a bit like you are using the online documentation code.
But the following assignment is missing:
var lastRow = sheet.getLastRow();
or just replace lastRow with sheet.getLastRow()
Update:
I assume that the following solution should work if you actually have any duplicates in your column B:
function hasDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Customer and form Details");
var array = sheet.getRange('B2:B' + sheet.getLastRow()).getValues();
var valuesSoFar = [];
for (var i = 0; i < array.length; ++i) {
var value = array[i][0];
if (value in valuesSoFar) {
SpreadsheetApp.getUi().alert('Hello, world!');
} else {
valuesSoFar.push(value);
}
}
}
If it isn't working yet, I am sorry.
As long as you can not provide any information on what kind of error you are running into, I will not look into this anymore.
I'm working on exercism question and am stuck on one of the jasmine-node based tests, which says that I should be able to generate 10000 random names without any clashes (e.g. 2 randomly generated names match). This is the test:
it('there can be lots of robots with different names each', function() {
var i,
numRobots = 10000,
usedNames = {};
for (i = 0; i < numRobots; i++) {
var newRobot = new Robot();
usedNames[newRobot.name] = true;
}
expect(Object.keys(usedNames).length).toEqual(numRobots);
});
What I think I need to do is:
Create an array to hold all the names (robotNames),
Each time a name is generated, check if it exists in the array,
If it does, generate another name,
If it doesn't, add it to the array.
And here is my code so far...
"use strict";
var robotNames = [];
var name;
var Robot = function() {
this.name = this.generateName();
};
Robot.prototype.generateName = function() {
var letters = "";
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var numbers = "";
var digits = "0123456789";
// generate random characters for robot name...
for( var i=0; i < 2; i++ ) {
letters += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
};
for( var i=0; i < 3; i++ ) {
numbers += digits.charAt(Math.floor(Math.random() * digits.length));
};
name = letters+numbers;
// Loop through array to check for duplicates
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
this.generateName();
return;
} else {
robotNames.push(name);
}
}
return name;
};
Robot.prototype.reset = function() {
this.name = this.generateName();
};
module.exports = Robot;
The test fails with an error message: "Expected 9924 to equal 10000."
The '9924' number is slightly different each time I run the test. I'm thinking this means the generateName function is eventually generating 2 matching random names. It seems as though my loop for checking duplicates is not being run and I'm not sure why.
I have tried a couple of different versions of the loop but with no success. So my questions is a) is my approach correct and there is something wrong with the syntax of my loop? or b) have I got the wrong idea about how to check for duplicates here?
Any pointers appreciated, thanks.
The problem is in this bit:
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
this.generateName();
return;
} else {
robotNames.push(name);
}
}
...you probably only want to push your name if NONE of the names fail to match. Here you're adding it to the list as soon as you find ONE that doesn't match. You want something more like:
for(var i = 0; i < robotNames.length; i++) {
if (name == robotNames[i]) {
return this.generateName();
}
}
robotNames.push(name);
(actually, combined with the fact that you weren't even returning the recursive call to this.generateName(), I'm not sure how your program could work...)
Find a library with an implementation for Sets. Collections.js is a good example.
One property of a set is that it doesn't have duplicates. So when you add a value to a set it will look for a duplicate and then add the value if no duplicate exists.
I have 2 spreadsheet in my Drive. I want to pull data from a cell in 1 spreadsheet and copy it in another.
The spreadsheet "TestUsage" will sometimes have data in column A, but none is column B.
I would like so that when I open the spreadsheet, it would populate that empty cell in sheet "TestUsage" from sheet "TestParts".
Here is my code:
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 4);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 3);
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
Logger.log(rangeTestUsage[i][1]);
Logger.log(rangeTestParts[j][1]);
rangeTestUsage[i][1] = rangeTestParts[j][1];
break;
}
}
}
}
}
The problem with this is this doesn't do anything:
rangeTestUsage[i][1] = rangeTestParts[j][1];
I know there must be a method that can get data from one range to another.
Please let me know if I am totally incorrect or I am on the right path.
the statement
"this doesn't do anything:"
rangeTestUsage[i][1] = rangeTestParts[j][2];
is not really true... and not really false neither..., actually it does assign the value to rangeTestUsagei but you dont see it because it is not reflected in the spreadsheet.
Both values are taken from the Sheet using getValues so at that time they are both array elements.
What is missing is just writing back the array to the sheet using the symetrical statement setValues()
Give it a try and don't hesitate to come back if something goes wrong.
EDIT :
I didn't notice at first that you were using getSheetValues instead of getValues (simply because I never use this one)... the only difference is that getValues is a method of the range class while yours belongs to the sheet class; the syntax is similar in a way, just use
Sheet.getRange(row,col,width,height).getValues()
it takes one word more but has the advantage to have a direct equivalent to set values
Sheet.getRange(row,col,width,height).setValues()
Serge insas has a good explanation of why your code doesn't work and hints at the solution below.
I recommend you use an array to store the updated values of column B that you want then set the entire column at the end.
Modifying your code...
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 2);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 2);
var colB = [];
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
var matched = false;
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
//Logger.log(rangeTestUsage[i][1]);
//Logger.log(rangeTestParts[j][1]);
colB.push([rangeTestParts[j][1]]); // push the value we want into colB array
matched = true;
break;
}
}
if(!matched) // this is in case you don't have a match
colB.push([""]); // incase we don't have a matching part
} else {
colB.push([rangeTestUsage[i][0]]); // we already have a value we want so just add that to colB array
}
}
sTestUsage.getRange(2,2,lastRowTestUsage).setValues(colB); // update entire column b with values in colB array
}