Google Apps Script; how to solve TypeError: Cannot read properties of undefined (reading '0')? - google-apps-script

I have a Google sheet which I follow stock prices Daily; every day I get the stock prices online and write them to the sheet via apps script. Now I want to check monthly P/L for each stock and create a sheet that shows monthly P/L for each stock.
I am trying to get the prices with dates for each stock in an array and then push the first and last entries for every month in "monthly" array. And then I am trying to write this monthly values in "Aylık P/L" sheet.
The code works fine till I added "for" statement to write the monthly datas to the sheet; it throws: "Error in Monthly: TypeError: Cannot read properties of undefined (reading '0')":
Here goes the code:
function aylikRapor() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Fiyat Çek');
var sheet2 = ss.getSheetByName('Günlük Fon Takip');
var sheet3 = ss.getSheetByName('Haftalık Fon Takip');
var sheet4 = ss.getSheetByName('Aylık Fon Takip');
var sheet5 = ss.getSheetByName('Funds Main');
var sheet6 = ss.getSheetByName('Aylık');
var sheet7 = ss.getSheetByName('Aylık P/L');
var pfs = sheet5.getRange(1,1).getValue(); // pfs = PortföydekiFonSayısı
var i = 1;
var f = 1;
var a;
var b;
try {
for(i=1; i<=pfs; i++) {
var colH = sheet2.getRange(1,3*i,1,1);
var harf = colH.getA1Notation().match(/([A-Z]+)/)[0];
var column = sheet2.getRange(harf+":"+harf);
// get lastFilledRow
var value = ''
const max = sheet2.getMaxRows()
var values = column.getValues()
values = [].concat.apply([], values)
for (row = max - 1; row > 0; row--) {
value = values[row]
if (value != '') { break }
}
lastRowD = row + 1;
Logger.log(lastRowD);
let veriler = sheet2.getRange(14,3*i-1,lastRowD-12,2).getValues();
let month = veriler[0][0].getMonth();
let monthly = [[veriler[0][0],veriler[0][1]]];
veriler.forEach( (row,index) => {
let test = new Date(row[0]).getMonth();
if( test !== month ) {
monthly.push([veriler[index-1][0],veriler[index-1][1]]);
monthly.push([row[0],row[1]]);
month = test;
}
}
)
Logger.log(monthly);
for (f=1, a=0, b=1; f <=monthly.length-1; f++, a+=2, b+=2){
sheet7.getRange(3+f,5*i-2).setValue(monthly[a][0]);
sheet7.getRange(3+f,5*i-1).setValue(monthly[a][1]);
sheet7.getRange(3+f,5*i).setValue(monthly[b][0]);
sheet7.getRange(3+f,5*i+1).setValue(monthly[b][1]);
}
}
}
catch(err) {
Logger.log("Error in Monthly: "+err);
}
}
I think there is a null value in "monthly" array; but I could not figure out how I can solve it!
Execution Log:
Yürütme günlüğü
09:49:37 Bildirim Yürütme işlemi başlatıldı
09:49:49 Bilgi 119.0
09:49:50 Bilgi [[Wed Aug 17 00:00:00 GMT+03:00 2022, 0.080777], [Wed Aug 31 00:00:00 GMT+03:00 2022, 0.082585], [Thu Sep 01 00:00:00 GMT+03:00 2022, 0.0831], [Fri Sep 30 00:00:00 GMT+03:00 2022, 0.08431], [Mon Oct 03 00:00:00 GMT+03:00 2022, 0.085019], [Mon Oct 31 00:00:00 GMT+03:00 2022, 0.096867], [Tue Nov 01 00:00:00 GMT+03:00 2022, 0.098144], [Wed Nov 30 00:00:00 GMT+03:00 2022, 0.115665], [Thu Dec 01 00:00:00 GMT+03:00 2022, 0.117343], [Fri Dec 30 00:00:00 GMT+03:00 2022, 0.126554], [Mon Jan 02 00:00:00 GMT+03:00 2023, 0.127234], [Thu Jan 12 00:00:00 GMT+03:00 2023, 0.111894], [, ]]
09:49:50 Bilgi Error in Monthly: TypeError: Cannot read properties of undefined (reading '0')
09:49:38 Bildirim Yürütme işlemi tamamlandı

I have fixed the problem:
As you can see from the execution log; for i=1 the first monthly array length is 12; during the for (f=1, a=0, b=1; f <=monthly.length-1; f++, a+=2, b+=2)
the max f should be 6; so after 6 there would be no values in the array.
I fixed the problem by the following amendment:
(f=1, a=0, b=1; f <=monthly.length/2; f++, a+=2, b+=2)
I hope the others could get some help in case problems like this.

Related

How do I enter multiple timestamps for multiple columns as they are modified?

I was able to modify if in any column within column 1 through 9 was modified the time stamp was entered on column 10 and last modified on column 11.
I know need to get granular and timestamp when each column is first modified, while maintaining the existing timestamp setting.
I believe this is as simple as an "if" statement but I can't seem to figure it out.
Here is what I have so far. This gives me the date and time any cell, between column 1 and 9 is first modified, on that row and returns date and time on column 10, then last modified date and time on column 11 if any changes are done.
Now i want to keep record of when column 1-9 are first modified as well, and enter them on the same sheet starting at column 12
function addTimestamp (e){
//variables
var startRow = 2;
var ws = "Daily Log"
var currentDate = 1
//get modified row and column
var row = e.range.getRow();
var col = e.range.getColumn();
if(([1,2,3,4,5,6,7,8,9].includes(col))&& row >= startRow
&&e.source.getActiveSheet().getName() ==ws){
var currentDate = new Date();
e.source.getActiveSheet().getRange(row, 11).setValue(currentDate);
if(e.source.getActiveSheet().getRange(row, 10).getValue()== ""){
e.source.getActiveSheet().getRange(row,10).setValue(currentDate);
} // END IF check if date created exists
} // END IF column, row, worksheet
}
Special thanks to #raygun who helped me in creating uniq driver IDs.
Explanation:
Your goal is:
Keep the datetime for which any cell in columns 1-9 is modified for the first time in column 10.
For every cell modified in columns 1-9 add a last modified stamp in columns 12-20 respectively. For example if column 1 is modified, update timestamp in column 12, if column 2 is modified update column 13 etc.
For performance reasons, it is always a good practice to store the code you are using often, in variables:
For example e.source.getActiveSheet() is used 4 times in your code and every time it is called, it consumes resources. You can instead store it in a variable and use that variable instead.
Solution:
function addTimestamp (e){
//variables
const startRow = 2;
const ws = "Daily Log"
//get modified row and column
const rng = e.range;
const as = rng.getSheet();
const row = rng.getRow();
const col = rng.getColumn();
if(col>=1 && col<=9&& row >= startRow && as.getName() ==ws){
const currentDate = new Date();
const rng_all = as.getRange(row, col+11);
const rng_ten = as.getRange(row, 10);
as.getRange(row,11).setValue(currentDate);
if(rng_all.getValue()== ""){
rng_all.setValue(currentDate)
}
if(rng_ten.getValue()== ""){
rng_ten.setValue(currentDate);
}
}
}
Side Note:
You don't need an installable trigger for this particular task. You can replace addTimestamp(e) with onEdit(e) (simple trigger) and it would also work. But it is up to you what you prefer, but it would be a good idea to read the differences (in the hyperlinks I provided).
Here's a function for logging all of your changes into a file:
function onMyEdit(e) {
e.source.toast('entry');
let sh = e.range.getSheet();
if (e.range.columnStart < 10) {
let f = DriveApp.getFileById('file id');
let c = f.getBlob().getDataAsString();
let ts = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "E MMM dd yyyy HH:mm:ss");
let o = c + ts + ',' + sh.getName() + ',' + e.range.rowStart + ',' + e.range.columnStart + ',' + e.oldValue + ',' + e.value + ',' + e.user.getEmail() + '\r\n';
f.setContent(o);
Logger.log(o);
}
}
Requires Installable Trigger
Data Table:
timestamp
sheetname
row
col
oldvalue
value
useremail
Sat Feb 13 2021 08:53:53 GMT-0700 (Mountain Standard Time)
Sheet1
5
4
30
55
Redacted
Sat Feb 13 2021 08:54:58 GMT-0700 (Mountain Standard Time)
Sheet1
9
4
32
55
Redacted
Sat Feb 13 2021 08:56:05 GMT-0700 (Mountain Standard Time)
Sheet1
14
7
62
new value
Redacted
Sat Feb 13 2021 08:56:45 GMT-0700 (Mountain Standard Time)
Sheet1
1
2
10
45
Redacted
Sat Feb 13 2021 08:56:52 GMT-0700 (Mountain Standard Time)
Sheet1
3
2
11
77
Redacted
Sat Feb 13 2021 09:10:16 GMT-0700 (Mountain Standard Time)
Sheet2
5
2
4
77
Redacted
Sat Feb 13 2021 09:16:08 GMT-0700 (Mountain Standard Time)
Sheet5
8
4
undefined
666
Redacted
Sat Feb 13 2021 09:18:04 GMT-0700 (Mountain Standard Time)
Sheet5
13
7
undefined
66
Redacted
Sat Feb 13 2021 09:19:44 GMT-0700 (Mountain Standard Time)
Sheet5
9
3
undefined
66
Redacted
Sat Feb 13 2021 09:20:29 GMT-0700 (Mountain Standard Time)
Sheet5
11
7
undefined
kk
Redacted
Sat Feb 13 2021 09:22:21 GMT-0700 (Mountain Standard Time)
Sheet1
1
1
undefined
undefined
Redacted
Here's a utility for loading a simple csv into a spreadsheet:
function loadCSVintoSpreadsheet() {
const ss = SpreadsheetApp.getActive();
const ui = SpreadsheetApp.getUi();
const resp1 = ui.prompt('Select Sheet Name', 'Enter destination sheet name', ui.ButtonSet.OK_CANCEL);
if (resp1.getSelectedButton() == ui.Button.OK && resp1.getResponseText().length > 0) {
const sh = ss.getSheetByName('Sheet1');
sh.clearContents();
const resp2 = ui.prompt('CSV FileId', 'Enter file id of csv', ui.ButtonSet.OK_CANCEL);
if (resp2.getSelectedButton() == ui.Button.OK && resp2.getResponseText().length > 0) {
const file = DriveApp.getFileById(resp2.getResponseText());
const csv = file.getBlob().getDataAsString();
let csvA = Utilities.parseCsv(csv)
sh.getRange(1, 1, csvA.length, csvA[0].length).setValues(csvA);
}
}
}

How to build a date from separate data with google app script?

In my first sheet I have this:
E21: 1 juin 2020
G21: 09
H21: h
I21: 02
I want to keep this in the first sheet but In the second sheet I want this:
B3: 2020-06-01 09:02
I try this:
// I recover my sheet
var ss_evenement = SpreadsheetApp.getActive().getSheetByName('Evenement');
// I get my date
var dateDebut = ss_evenement.getRange('E21').activate().getValue();
// I put my date in sting
var dateDebutSTR = dateDebut.toString();
// I found the day of my date : with getDay I have the day of the week
var jourDebut = dateDebutSTR.substring(8,10);
// I found the month
var moisDebut = dateDebut.getMonth().toString();
// I found the Year
var anneeDebut = dateDebut.getYear().toString();
// I read the hours
var heureDebut = ss_evenement.getRange('G21').activate().getValue();
// I read the minutes
var minuteDebut = ss_evenement.getRange('I21').activate().getValue();
// I recreate my date
var dateheureDebut = new Date(anneeDebut, moisDebut, jourDebut, heureDebut, minuteDebut, "00", "00");
// I select the sheet to writing
var ss_calendrier = SpreadsheetApp.getActive().getSheetByName('Calendrier');
// I write my new date in my new sheet
ss_calendrier.getRange('B4').activate().setValue(dateheureDebut);
In debug I have:
dateDebut->Date (541813791)->Thu Apr 02 2020 03:00:00 GMT-0400 (EDT)
dateDebutSTR->String->"Thu Apr 02 2020 03:00:00 GMT-0400 (EDT)"
jourDebut->String->"02"
moisDebut->String->"3"
anneeDebut->String->"2020"
heureDebut->String->"09"
minuteDebut->String->"02"
dateheureDebut->Date (541917478)->Thu Apr 02 2020 09:02:00 GMT-0400 (EDT)
And in true, in the second file I have:
01/06/2020 06:02
The hour et the format is not good.
Try this:
function whoknowswhat() {
var mA=['jan','feb','mar','apl','may','jun','jul','aug','sep','oct','nov','dec'];
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Evenement');
var dt=sh.getRange('E21').getValue().trim();
var year=dt.slice(6);
var month=mA.indexOf(dt.slice(2,5));
var day=parseInt(dt.slice(0,2));
var hour=sh.getRange('G21').getValue().trim();
var minute=sh.getRange('I21').getValue().trim();
var dateheureDebut=new Date(year,month,day,hour,minute, 0);
var ss_calendrier=SpreadsheetApp.getActive().getSheetByName('Calendrier');
ss_calendrier.getRange('B4')setValue(dateheureDebut);
}
You can use the method getDisplayValue() to obtain the value that is displayed in the Sheet. Afterwards, it is easier to parse it and generate the final string from its data. You can see an example using this technique below:
function copyDate() {
var evenementSheet = SpreadsheetApp.getActive().getSheetByName('Evenement');
var months = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"];
var dateValues = evenementSheet.getRange('B7').getDisplayValue().split(' ');
var anne = dateValues[2];
var jour = dateValues[0];
var mois = months.indexOf(dateValues[1])+1;
var heure = evenementSheet.getRange('G21').getDisplayValue();
var minute = evenementSheet.getRange('I21').getDisplayValue();
var result = Utilities.formatString('%s-%s-%s %s:%s',
anne,
mois.length > 1 ? mois : '0' + mois,
jour.length > 1 ? jour : '0' + jour,
heure,
minute);
var calendrierSheet = SpreadsheetApp.getActive().getSheetByName('Calendrier');
calendrierSheet.getRange('B4').setValue(result);
}

Find and match a date in array

I'm trying to make a function in order to find & match the date of day in a array.
But for a weird reason, if two dates are matching, my function never find the match.
So..., if I change my date by an other string (ex : "foo" match with "foo"), it's work.
Here my code, and then the log return. Have you an idea why my script can't match two equal dates ?
Thank you !
function hideBeforeToday(values, value)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(ss.getSheetByName(roadmapSS))
var range = sheet.getRange(4, 11, 1, sheet.getLastColumn())
var values = range.getValues();
Logger.log(values);
var value = moment().set({hour:0,minute:0,second:0,millisecond:0}).utc().toDate();
Logger.log("date", value);
for(var i = 0; i < values.length; i++) {
if(values[i].indexOf(value) > -1) {
Logger.log("data found");
}
}
}
[18-01-11 13:49:33:065 CET] Logger.log([arrayValues, [[[Wed Jan 10 00:00:00 GMT+01:00 2018, Thu Jan 11 00:00:00 GMT+01:00 2018]]]]) [0 secondes]
[18-01-11 13:49:33:067 CET] Logger.log([dateOfDay, [Thu Jan 11 00:00:00 GMT+01:00 2018]]) [0 secondes]
[18-01-11 13:49:33:070 CET] Script exécuté [durée totale d'exécution : 0,535 secondes]
Look at this example:
var date1 = new Date("Thu Jan 11 00:00:00 GMT+01:00 2018");
var dates = [date1];
console.log(date1); // prints 2018-01-10T23:00:00.000Z
console.log(dates.indexOf(date1)); // prints 0
While this:
var date1 = new Date("Thu Jan 11 00:00:00 GMT+01:00 2018");
var dates = [new Date("Thu Jan 11 00:00:00 GMT+01:00 2018")];
console.log(date1); // prints 2018-01-10T23:00:00.000Z
console.log(dates.indexOf(date1)); // prints -1
I don't know what data you have in the spreadsheet, if it is a string or a Date object, but try converting both to the same type before comparing. Check their types and perhaps use the toISOString() function?

Google Apps Script: comparing dates

I'm having a weird issue comparing dates in my Google Apps Script.
For this, my sheet has a date in cell.getValue() so on the
e.range.setNote(cell.getValue() + " --- " + startDate);
line, the note shows two dates that appear identical, but they aren't because the following if statement is not satisfied. Am I missing something here with the comparisons? Thanks.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sss = ss.getSheetByName("Sheet1");
var row = e.range.getRow();
var startDate = sss.getRange(row, 3).getValue(); //Wed Aug 10 2016 00:00:00 GMT-0600 (GALT)
var cell = ss.getSheetByName("Sheet2").getRange(1, 1);
e.range.setNote(cell.getValue() + " --- " + startDate); //Wed Aug 10 2016 00:00:00 GMT-0600 (GALT) --- Wed Aug 10 2016 00:00:00 GMT-0600 (GALT)
if (cell.getValue() === startDate){
cell.setBackground('blue');
}
}
I think you need to compare as dates https://stackoverflow.com/a/493018/1393023
Replace
cell.getValue() === startDate
to
cell.getValue().getTime && startDate.getTime && cell.getValue().getTime() === startDate.getTime()

Values Match if not being met :S

function unupdatedRows () {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSheet = ss.getSheets()[0];
var numRows = formSheet.getLastRow();
var lastRow = formSheet.getRange(numRows,1);
var lastDate = lastRow.getValue();
Logger.log(lastUpdate());
Logger.log(lastDate);
if (lastDate == lastUpdate()){
Logger.log("MATCH! Sheet is up to date");
}
};
Logger output:
[14-06-24 16:12:53:188 EDT] Mon Jun 23 01:50:10 GMT-04:00 2014
[14-06-24 16:12:53:188 EDT] Mon Jun 23 01:50:10 GMT-04:00 2014
I should be seeing "MATCH! Sheet is up to date" in the logger too. Why isn't the if statement being met?
Avoid checking equality on date objects, they will never be equal (you can use > and < comparison though), do it on their string representation for instance or from any derived value (year, month, day, hours...) or even on their native values (millisec).