The script below does not work as expected. Can anybody help me to check if I missed anything? Gives me error TypeError: Cannot call method "getValue" of null. I run the script again and it goes through but doesn't change anything on the file.
Below are the parts of the script and how they should work:
First part is to automatically get data from the CSV Reports in Keyword Inspector. I have this "Settings" sheet where it has the password, email, and URL Links to the reports. > first part works fine.
Second part is to paste data in the "GetCSV" sheet. But first we must check if we have data in the first three columns, if there is any existing data we must move all data to the right. We paste what we get from the .csv file in "GetCSV" sheet, in Column 2, Row 2. In Column 1, row 2 we paste date and time format starting from A2 down to the last row with data:
setValue(Utilities.formatDate(new Date(), "GMT", "MM-dd HH:mm AM/PM"));
Third part, we copy the same data we updated to its final destination "Data" sheet, starting from the first empty row.
I am not very savvy with the Java script and I already have this script in place, I just need to modify it a bit based on my needs.
function getCsv(){
//populateSheetWithCSV("http://simple.keywordinspector.com/simple/download.php?id=12612", "###.######gmail.com", "Abc12345")
//;
//var settingsSheet= SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var passCell = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("password");
//getRange("B3");
var emailCell = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("email");
Logger.log(emailCell)
var cp = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_512, passCell.getValue());
var sp = '';
for (i = 0; i < cp.length; i++) {
var byte = cp[i];
if (byte < 0)
byte += 256;
var byteStr = byte.toString(16);
// Ensure we have 2 chars in our byte, pad with 0
if (byteStr.length == 1) byteStr = '0'+byteStr;
sp += byteStr;
}
var payload =
{
"email" : emailCell.getValue(),
"password" : "",
"p" : sp
}
Logger.log(cp);
//return;
var options =
{
"method" : "post",
"payload" : payload,
"followRedirects" : false
};
var login = UrlFetchApp.fetch("http://simple.keywordinspector.com/simple/process_login.php" , options);
login.getContentText()
var sessionDetails = login.getAllHeaders()['Set-Cookie'];
Logger.log(sessionDetails)
var csvUrlId = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("URL_Id").getValue();
var csvUrls = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("URLS").getValues();
Logger.log(csvUrls[csvUrlId-1]);
var downloadCsv = UrlFetchApp.fetch(csvUrls[csvUrlId-1] ,//"http://simple.keywordinspector.com/simple/download.php?id=12612",
{"headers" : {"Cookie" : sessionDetails},
"method" : "post",
"payload" : payload
});
//Logger.log(downloadCsv.getContentText())
var csvContent = Utilities.parseCsv(downloadCsv.getContentText());
var sheet= SpreadsheetApp.getActiveSpreadsheet().getSheetByName("GetCSV");
var fisrtEmptyColumn
for (i=1 ; i<1024 ; (i=i+2)) {
var range = sheet.getRange(2,i,1,1);
if (range.isBlank()) {
firstEmptyColumn = i
break;
}
}
Logger.log(firstEmptyColumn);
for (i = firstEmptyColumn ; i>3 ; i=i-3){
Logger.log(i)
var rangeToCopy =sheet.getRange(1, i-3 ,sheet.getMaxRows(),3);
rangeToCopy.copyTo(sheet.getRange(1, i));
}
var cell = sheet.getRange(1,2)
var A1not = sheet.getRange(3, 2, sheet.getMaxRows()-2, 1).getA1Notation();
cell.setFormula("=COUNTA("+A1not+")");
//
for (i = firstEmptyColumn ; i>4 ; i=i-2){
for (j = 3; j < sheet.getMaxRows()-2 ; j++){
var cell = sheet.getRange(j,1);
cell.setValue(Utilities.formatDate(new Date(), "GMT", "MM-dd HH:mm AM/PM"));
sheet.getRange(2, 2, csvContent.length /* rows */, csvContent[0].length /* columns */).setValues(csvContent);
firstEmptyColumn = firstEmptyColumn +2 ;
if (firstEmptyColumn>21) {
firstEmptyColumn = 21
}
Logger.log(firstEmptyColumn);
var sourcesheet= SpreadsheetApp.getActiveSpreadsheet().getSheetByName("GetCSV");
var SourceRange = sourcesheet.getRange(3, 1, sourcesheet.getLastRow(), sourcesheet.getLastColumn());
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");;
var targetRange = target.getRange(target.getLastRow() + 1, 2);
SourceRange.copyTo(targetRange)
}
var cell = sourcesheet.getRange("A1")
for (j = target.getlastrow() +1; j < sheet.getMaxRows()-2 ; j++){
var targetRange2 = target.getRange(j,1);
cell.copyto(targetRange2)
}
}
}
Related
I have a program that filters and updates data from an existing sheet.
The program works as follows:
1. Find and filter out the required value
2. Enter data in [Adjustment] column then update to database in Record sheet.
I tried to try but my program doesn't seem to work.
I tried to edit the program code but when run it will affect the other columns and the [adjustment] column value is entered wrong.
This is my link program
function Searchold(){
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var shtRecords = ss. getSheetByName ("RECORD");
var shtForm = ss. getSheetByName ("TEST") ;
var records = shtRecords. getDataRange () . getValues ();
var sField = shtForm. getRange ("A3").getValue ();
var sValue = shtForm.getRange ("A6").getValue();
var sCol = records [0].lastIndexOf(sField);
var results = records.filter(function(e){return sValue == e[sCol] });
if(results.length==0){SpreadsheetApp.getUi().alert("not found values");}
else{
shtForm.getRange(9,1,results.length,results[0].length).setValues(results);
}
}
function Updatenew(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var shtRecords = ss.getSheetByName("RECORD");
var shtForm = ss.getSheetByName("TEST");
var LastRow = shtForm.getRange("A8").getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow();
var newData = shtForm.getRange(9,1,LastRow -1,7).getValues();
for(var i =0; i<newData.length;i++){
var oldData= shtRecords.getDataRange().getValues();
for(var j= 0;j<oldData.length;j++){
if(newData[i][0] ==oldData[j][0]){
var newData2 = [newData[i]];
shtRecords.getRange(j + 1,1,1,newData2[0].length).setValues(newData2);
}
}
}
}
Can you help me with the update program? Sincerely thank you
Modification points:
When I saw your showing script of Updatenew, I think that each row of var oldData = shtRecords.getDataRange().getValues() is used in each loop of for (var i = 0; i < newData.length; i++) {}. By this, each row is overwritten by each row of newData. By this, all searched rows in "RECORD" sheet are the same value. I thought that this might be the reason for your issue.
var oldData = shtRecords.getDataRange().getValues(); can be used one call.
In order to avoid this issue by modifying your script, as one of several methods, how about the following modification?
From:
for (var i = 0; i < newData.length; i++) {
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
if (newData[i][0] == oldData[j][0]) {
var newData2 = [newData[i]];
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
}
}
}
To:
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
for (var i = 0; i < newData.length; i++) {
if (newData[0][0] == oldData[j][0]) {
var newData2 = newData.splice(0, 1);
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
break;
}
}
}
Note:
At the above modification, setValues is used in a loop. In this case, the process cost becomes high. If you want to reduce the process cost of the script, how about using Sheets API? When Sheets API is used, how about the following modification? Please enable Sheets API at Advanced Google services.
To
var temp = newData.slice();
var data = shtRecords.getDataRange().getValues().reduce((ar, r, i) => {
if (temp[0][0] == r[0]) {
var t = temp.splice(0, 1);
t[0][2] = Utilities.formatDate(t[0][2], Session.getScriptTimeZone(), "dd/MM/yyyy");
t[0][4] = Utilities.formatDate(t[0][4], Session.getScriptTimeZone(), "dd/MM/yyyy");
ar.push({ range: `'RECORD'!A${i + 1}`, values: t });
}
return ar;
}, []);
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ss.getId());
This question already has answers here:
Long processing time likely due to getValue and cell inserts
(2 answers)
Closed 8 months ago.
I have a code working fine but not optimized (I am new to Google App script).
This code is doing the following :
Get data from external URL
Filter the data
Parse data in sheets contained in a folder
Change columns header
Appen content in a specific column
function myfunction() {
var keywords = ["valuetoremove1", "valuetoremove2"]; // filter the column "C".
// Retrieve CSV data.
var csvUrl = "https://myurl";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent, ";");
// Retrieve Spreadsheet and put the CSV data.
var root = DriveApp.getFoldersByName("Folder1");
while (root.hasNext()) {
var folder = root.next();
var files = folder.getFiles();
while (files.hasNext()) {
var spreadsheet = SpreadsheetApp.open(files.next());
var name = spreadsheet.getName().toUpperCase();
var values = csvData.reduce((ar, r) => {
if (!keywords.some(e => r[2].toUpperCase().includes(e.toUpperCase())) && r.join("").toUpperCase().includes(name)) {
ar.push(r);
}
return ar;
}, []);
if (values.length == 0) continue;
var sheet = spreadsheet.getSheets()[0];
sheet.clearContents().getRange(2, 1, values.length, values[0].length).setValues(values);
// modify column titles
var cell = sheet.getRange(1,1);
cell.setValue("Column1");
var cell = sheet.getRange(1,2);
cell.setValue("Column2");
var cell = sheet.getRange(1,3);
cell.setValue("Column3");
var cell = sheet.getRange(1,4);
cell.setValue("Column4");
var cell = sheet.getRange(1,5);
cell.setValue("Column5");
// Modify column E
var dataRange = spreadsheet.getDataRange().getValues();
var colData = [];
for (var i = 1; i < dataRange.length; i++) {
colData.push(dataRange[i][0]);
}
for (var i = 0; i < colData.length; i++) {
// Get column E
var comments_cell = spreadsheet.getDataRange().getCell(i + 2, 5).getValue();
// Append
spreadsheet.getDataRange().getCell(i + 2, 5).setValue('<button type="button">Button</button>');
}
}
}
}
It works but it takes ages, especially the last part, the lines are being changed one by one.
Is there any way to make it much faster ?
Thanks
Try (after // Modify column E)
var dataRange = spreadsheet.getDataRange().getValues()
for (var i = 1; i < dataRange.length; i++) {
// Get column E
var comments_cell = dataRange[i][4];
dataRange[i][4] = '<button type="button">Button</button>'
}
spreadsheet.getDataRange().setValues(dataRange)
I'm helping an educational project.
The following is the request:
the words-sentences in 1 row from a google spreadsheets list should be sent automatically to the discord text channal every day. The next day, a line below. When the whole list is finished, it should go back to the beginning and send it again. and write it on a new line after each column.
2nd request: same but this time 2 rows should be sent every day.
Number of columns Generally the same 2 or 3.
this is the code i found works, but that's not what i wanted. this code is for:"a range of cells".
How do I get it to send the next line every day? I will set the code to run once a day with Trigger from the menu. But how will it know which line it sent yesterday, etc.?
Unfortunately, I couldn't do exactly what I wanted. I will be glad if you help
(I'm an IT person, but I don't have any coding knowledge. I understand the code when I see it, but I can't write it.)
enter image description here
function postMessageToDiscord(message) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Loot");
var range = sheet.getRange("A1:C3");
var numRows = sheet.getLastRow()-1; // Number of rows to process
var data = range.getValues();
var result = '';
for (var i = 0; i < data.length; i++) {
var d = data[i];
for (var j = 0; j < d.length; j++) {
result = result.concat(d[j]);
}
}
message = message || result ;
var discordUrl = 'webhook xxx';
var payload = JSON.stringify({content: message});
var params = {
method: "POST",
payload: payload,
muteHttpExceptions: true,
contentType: "application/json"
};
var response = UrlFetchApp.fetch(discordUrl, params);
Logger.log(response.getContentText());
}
Edit:
function postMessageToDiscord(message) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("b1");
var propertyServ = PropertiesService.getScriptProperties();
var properties = propertyServ.getProperties(); //get script properties
var row = 1; // set initial row
var incRow = 4; // how much row
if(Object.getOwnPropertyNames(properties).length != 0){ //this will check if the properties object is not empty
row = parseInt(properties["row"]) + incRow; //increase row
}
var range = sheet.getRange(row, 1, incRow, 6);
var values = range.getValues();
var result = '';
for (var i = 0; i < values.length; i++) {
var d = values[i];
for (var j = 0; j < d.length; j++) {
result = result.concat(d[j]);
}
}
message = message || result ;
var discordUrl = 'https://discord.com/api/webhooks xxx';
var payload = JSON.stringify({content: message});
var params = {
method: "POST",
payload: payload,
muteHttpExceptions: true,
contentType: "application/json"
};
var response = UrlFetchApp.fetch(discordUrl, params);
Logger.log(response.getContentText());
propertyServ.setProperty("row", row); //save the current row of processed line
}
As mentioned by Cooper, you can use the Properties Service of Google Apps Script to save the processed range in your code.
Here I have an example which you can incorporate to your code.
EDIT
Revised code to process 3 rows and print each row per line.
Test Data:
Code:
function propertyServExample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Loot");
var propertyServ = PropertiesService.getScriptProperties();
var properties = propertyServ.getProperties(); //get script properties
var row = 1; // set initial row
if(Object.getOwnPropertyNames(properties).length != 0){ //this will check if the properties object is not empty
row = parseInt(properties["row"]) + 3;
}
var range = sheet.getRange(row, 1, 3, 2);
var values = range.getValues();
var str = '';
values.forEach(val => {
str = str + val.join('') + '\n';
})
Logger.log(str);
propertyServ.setProperty("row", row); //save the current row of processed line
}
Each run will get the next row.
Run 1:
Run 2:
Run 3:
Reference:
Properties Service
Click here for Sample Sheet
I need a solution that matches a cell value (Sheet1! Q5) to a range in another tab/sheet (NegotiationData! A1:O1) and paste the relevant data fetched from the first sheet under the designated columns of the second sheet under the matched value.
For example, if Sheet1!Q5 matches with the name in NegotiationData! A1 then do the following
Fetch Sheet1! R6 and paste in NegotiationData!A3:A
Fetch Sheet1! Q6 and paste in NegotiationData!B3:B
Fetch Sheet1! Q7 and paste in NegotiationData!C3:C
Also, each time the script runs it should not overwrite data but find the next empty row and paste the values.
I have an incomplete script that I'm trying to achieve the above from my research from various posts but since I'm just a week old to coding I'm not able to go any further than where I have got with the below script.
I'm not finding how to match the value and fetch the relevant data and paste them below the matched value.
Please help!
The Incomplete / Incorrect Script (File Name: NegotiationSubmit)
function submitNegotiation() {
var sh, id, v, estNum, negotiation, negoNotes, i;
sh = SpreadsheetApp.getActive();
id = sh.getSheetByName('Sheet1').getRange('Q5').getValue();
v = sh.getRange('R6').getValue();
estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);
negoNotes = sh.getRange('Q7').getValue();
negotiation =sh.getSheetByName('NegotiationData').getRange('A1:O');
if(v && estNum) {
negotiation.setValues(negotiation.getValues()
.map(function (r, i) {
if (r[0] == id) {
r[1] = v;
r[2] = estNum;
r[3] = negoNotes;
}
return r;
})
)
}
}
How about this modification?
Modification points :
Retrieve values of "Q5:R7" at once, and the values are converted to the import values.
Use the destructuring assignment for retrieving each value.
Import the converted values using the number of column retrieved by ids[0][i] == id.
Modified script :
function submitNegotiation() {
var id, estNum, v, negoNotes;
var sh = SpreadsheetApp.getActive();
var values = sh.getSheetByName('Sheet1').getRange("Q5:R7").getValues();
[id] = values[0];
[estNum, v] = values[1];
[negoNotes] = values[2];
// estNum = Number(estNum.split(" ")[1]); // If you want to use "estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);", please use this line.
var sh2 = sh.getSheetByName('NegotiationData');
var ids = sh2.getRange("A1:O1").getValues();
for (var i=0; i<ids[0].length; i++) {
if (ids[0][i] == id) {
sh2.getRange(sh2.getLastRow() + 1, i + 1, 1, 3).setValues([[v, estNum, negoNotes]]);
}
}
}
Note :
I was confused to the following points.
In your script, estNum is Number(sh.getRange('Q6').getValue().split(" ")[1]);. But in your sample spreadsheet, Estimate 1 of cell "Q6" is used.
I commented this in modified script.
In your sample spreadsheet, "Story ID" is 1. But in your script, it's US-001 of cell "R6".
In this modified script, US-001 of cell "R6" was used.
If I misunderstand your question, I'm sorry.
Edit :
function submitNegotiation() {
var id, estNum, v, negoNotes;
var sh = SpreadsheetApp.getActive();
var values = sh.getSheetByName('Sheet1').getRange("Q5:R7").getValues();
[id] = values[0];
[estNum, v] = values[1];
[negoNotes] = values[2];
estNum = Number(estNum.split(" ")[1]); // If you want to use "estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);", please use this line.
var sh2 = sh.getSheetByName('NegotiationData');
var ids = sh2.getRange("A1:O1").getValues();
for (var i=0; i<ids[0].length; i++) {
if (ids[0][i] == id) {
var temp = sh2.getRange(1, i + 1, sh2.getLastRow(), 3).getValues();
for (var j=temp.length-1; j>=0; j--) {
if (temp[j].join("") != "") {
sh2.getRange(j + 2, i + 1, 1, 3).setValues([[v, estNum, negoNotes]]);
break;
}
}
}
}
}
I realize that this does not resemble your code. Sorry about that. I'm learning too. But I'm putting it up anyway to provide an alternative method that includes finding the last row of the appropriate column...
function submitNegotiation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var negotiationData = ss.getSheetByName('NegotiationData');
// Sheet1 variables
var user = sheet1.getRange('Q5').getValue();
var storyId = Number(sheet1.getRange('R6').getValue().split("-")[1]);
var estimateNum = sheet1.getRange('Q6').getValue();
var note = sheet1.getRange('Q7').getValue();
var pointers = [storyId, estimateNum, note];
// NegotiationData variables
var range = negotiationData.getDataRange().getValues();
var columns = negotiationData.getLastColumn();
var users = negotiationData.getRange(1, 1, 1, columns).getValues();
for(var i = 0; i < columns; i++) {
// match user with users to get column number
if(users[0][i] == user) {
var col = negotiationData.getRange(1, i + 1).getColumn();
// count rows in col
var rowCount = 1;
for(var i = 1; i < range.length; i++) {
if (range[i][col - 1] != "") {
rowCount++;
}
}
// assign pointers
var newRow = rowCount + 1;
for(var j = 0; j < pointers.length; j++) {
negotiationData.getRange(newRow, col, 1, 1).setValue(pointers[j]);
col++;
}
}
}
}
The code below takes the same range of data from multiple files in a folder. However, it only uses data in that range if it meets a criteria. So, the returned values from each file vary in height.
I have put a ??? indicating where I need the height of the range I am writing that data to to change for each loop based on the height of the data I am collecting from each file.
function getAllData() {
var folder = DocsList.getFolderById("folderid");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base")
sheet.clearContents();
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet 1");
var lastLine = sheet2.getLastRow();
var values = sheet2.getRange('A3:J').getValues();
var formulas = sheet2.getRange('A3:J').getFormulas();
var data = [];
for(var row = 0 ; row < (values).length ; row++){
var lastrow = sheet.getLastRow()+1;
if (values[row][0] != '') {
for(var col = 0 ; col < formulas[row].length ; col++){
if(formulas[row][col] != '')
{values[row][col] = formulas[row][col]};
data.push(values[row]);}
if(data.length > 0)
sheet.getRange(lastrow, 1, ???, data[0].length).setValues(data);
}
}
};
}}
There's only 1 valid value in the "???", and it is "data.length", do you really want to setValues() everytime in the loop? Shouldn't it be better to build all the data then output all at once?