I have a googlescript which creates a form that allows for existing entries to be edited, or for new entries to be added to a google spreadsheet. If it's a new entry, it takes the field values from the google form and places them into the last row of the google spreadsheet.
var lastCell = sheet.getLastRow(); //entry holds value of current last row position
var entry = sheet.getLastRow()+1; //entry holds value of new last row position
var IDCell = sheet.getRange("C"+entry); //IdCell is in column C, last row of table
The above code is fine..
The part I am hoping to get some help with is the part where I set IDCell to equal ((IDCell of the previous row) +1)
Something like:
IDCell.setValue((sheetData [lastCell][2]) +1);
but this gives me errors "Error encounter: cannot read property "2" from undefined"
Was able to solve on my own:
var lastcell = sheet.getLastRow();
var entry = sheet.getLastRow()+1;
var IDCell = sheet.getRange("C"+entry);
var prev_ID = sheet.getRange("C"+lastcell);
var prev_IDV = prev_ID.getValue();
IDCell.setValue((prev_IDV)+1);
This might have been obvious to some, but I am very new at JavaScripts so it wasn't immediately clear
Related
My information is set in columns (A to E), info paste in 2 rows (A2:A3, B2:B3...). What I want to happen is when info set in last column E (E2:E3, E5:E6 and more), new info paste with a new row in column A (A5:A6, B5:B6).
My info is in the spreadsheet:
This code paste info doesn't work correctly:
function submitData1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form"); //Form Sheet
var datasheet = ss.getSheetByName("Job"); //Data Sheet
//Input Values
var values1 = [[formSS.getRange("B6").getValue()], [formSS.getRange("B7").getValue()]];
var sr = 1
var sc = 1
var nr = 2
var nc = 1
for (var i=1;i<1;i++)
var workingCell = datasheet.getRange(i,7);
{
if (workingCell ==null)
{datasheet.getRange(sr,datasheet.getLastColumn()+1,nr,nc).setValues(values1)
}
else {datasheet.getRange(datasheet.getLastRow()+1, sc, nr, nc).setValues(values1)
sc= sc+1}
}
}
Here is a sample replication code:
function submitData1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var datasheet = ss.getSheetByName("Job"); //Data Sheet
//Input Values
var values1 = [["Name X"], ["Job X"]];
var lastRow = datasheet.getLastRow();
var lastRow_ColCount = datasheet.getRange(lastRow,1,1,5).getValues().flat().filter(String).length;
Logger.log(lastRow);
Logger.log(lastRow_ColCount);
if(lastRow_ColCount==5){
//Add data on a new row, column A
datasheet.getRange(lastRow+2, 1,2,1).setValues(values1);
}
else{
//Add data on the next column in the same row
datasheet.getRange(lastRow-1, lastRow_ColCount+1,2,1).setValues(values1);
}
}
I used a fixed value for the input data during this replication.
Just replace the values from your original code that will be read from your form sheet
You can also remove the logs in the script, I just used them to debug the code
What it does?
Get the last row of the sheet, It will determine the last row that has a valid data
Get the data/values of the last row from column A to column E. Use Array.flat() to change 2-d array to 1-d array. Use Array.filter() to get non empty cells then count the data available in the last row.
If the current data available is 5, then the new data will be added on the new row, else the new data will be added on the next column
Output:
I currently have a code that does something very similar to this, but im not sure the small change i need to make to have it work correctly. Right now, the code below compares two rows of unique IDs and if the IDs are the same, it copies the cell in the "Comments" column to the other sheet.
function setComments() {
var ss = SpreadsheetApp.getActive(),
compare1 = "", compare2 = "",
outputSheet = ss.getSheetByName("Sheet2"),
sourceSheet = ss.getSheetByName("Sheet1"),
range1 = outputSheet.getDataRange(),
range2 = sourceSheet.getDataRange(),
lastCol1 = range1.getNumColumns(),
lastCol2 = range2.getNumColumns(),
values1 = range1.getValues(),
values2 = range2.getValues(),
// get the range of the titles
titleSection1 = outputSheet.getRange(1,1,1, lastCol1),
titleSection2 = sourceSheet.getRange(1,1,1, lastCol2),
// get the values from the titles
titles1 = titleSection1.getValues(),
titles2 = titleSection2.getValues(),
// get the column # for "ID" and "comment"
idCol1 = titles1[0].indexOf("ID"),
idCol2 = titles2[0].indexOf("ID"),
commentsCol1 = titles1[0].indexOf("comment"),
commentsCol2 = titles2[0].indexOf("comment");
// get the IDs from range1
for (i = 1; i < values1.length; i++) {
compare1 = values1[i][idCol1];
// get the IDs from range2
for (j = 1; j< values2.length; j++){
compare2 = values2[j][idCol2];
// if same ID, change the values array
if (compare1 == compare2) {
values1[i][commentsCol1] = values2[j][commentsCol2];
}
}
}
// set values based on the values array
range1.setValues(values1);
}
Instead, if there is a change made to any cell on sheet 1, it will find the identical cell based on unique ID in the other sheet and sync the change. What change do i need to make to have this work?
For example, if I change what the office is in the row of ID 1 of sheet 1, it will make the identical change for ID 1 in sheet 2.
Here is an example sheet of what im working with:
Sheet 1:
ID Comment Number Office Clinician
1 good 22345 Dallas
2 bad 12345 Denton
3 good 95954 Lubbock
4 bad 20204 FT.W
5 bad 11111 Denton
6 good 02944 Preston
Sheet 2:
ID Comment Number Office Clinician
1 good 22345 Dallas
3 good 95954 Lubbock
5 bad 11111 Denton
You have one data set on Sheet 1, and a subset of that data set on Sheet1. The key field is the "ID". If there is a change to data on Sheet 1, and the ID relating to that change is found on Sheet 2, then you want to update the relevant dataset on Sheet 2.
The key aspects of this answer are:
onEdit(e): This is an simple Trigger.
e.range: "range" is an Event Object. By using the attribute 'e'; it is possible to recover a substantial amount of information about the changes. In addition, the event objects can be further used to obtain more information - such as (in this case) the Row, Column and Sheet Name.
filter(String).length: Sometimes getting the last row of data is problematic. The answer gets all the data in Column A, and uses the Javascript "array.filter" method. In this case is simply counts the values as strings, andf the resulting value is equal to the Last Row of data.
the "IF" statement evaluates for several attributes:
Is the edit on Sheet1?
Is the edit on a row between the header and last row?
Is the edit in a column that contains data?
the operator is "&&" which requires that each of the attributes must return true.
targetdata.map(function(e){return e[0];});: the script gets ALL the data on Sheet2, but uses the Javascript array.map method to generate a subject of just the values in Column 1 (the ID column).
targetCol1.indexOf(sourceID);: The script uses the Javascript "array.indexOf"; if the ID is found, it will return the index number of the row on Sheet2; if the value isn't found, it will return "-1". The enables a logic statement to be written that will be executed only if the value is not "-1"
target.getRange(+result+1,1,1,sourceLC).setValues(sourcedatatocopy): the last lines of the script get the values in the edited row on Sheet 1, and then update the values on Sheet2. Note: it updates all the values for the matched ID - rather than identifying the changed field and updating only that field.
function onEdit(e) {
// setup spreadsheet and sheet names
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSN = "Sheet1";
var source = ss.getSheetByName(sourceSN);
var targetSN = "Sheet2";
var target = ss.getSheetByName(targetSN);
// get row, column and sheet of the edited sheet
var row = e.range.getRow();
var col = e.range.getColumn();
var editedsheet = e.range.getSheet().getSheetName();
// Logger.log("DEBUG: editedsheet = "+editedsheet)
// get the ID column from Sheet 1
var SourceCol1Vals = ss.getRange("A2:A").getValues();
var SourceLR = SourceCol1Vals.filter(String).length;
// assign value to last column
var sourceLC = 5;
// test for sheet, row range and column range
if (editedsheet == sourceSN && row >=2 && row <=SourceLR && col <=5){
//Logger.log("DEBUG: this is sheet 1 & the right row and the right Col")
// get the data for this row
var sourcedata = source.getRange(row,1,1,5).getValues();
// get the data from target
var targetdata = target.getDataRange().getValues();
// get only the ID column
var targetCol1 = targetdata.map(function(e){return e[0];});
// get the sourse ID
var sourceID = sourcedata[0][0];
// Logger.log("DEBUG: source ID = "+sourceID)
// search for source ID on the Target list
var result = targetCol1.indexOf(sourceID);
// Logger.log("DEBUG: id was found at "+result);
if (result !=-1){
// if -1 then couldn't find the ID, otherwise it returns the index number where it finds the match
// get the data for the Source ID
var sourcedatatocopy = source.getRange(row,1,1,sourceLC).getValues();
// update the darget for the sourceID data.
target.getRange(+result+1,1,1,sourceLC).setValues(sourcedatatocopy)
}
} else{
// Logger.log("DEBUG: not sheet 1 or right row or right col");
}
return;
}
I'm struggling to get javascript to run in Googlesheets to do the following:
Loop through column A:A comparing the above cell value to the below cell value
Where, for example say A2 isn't the same as A3 then insert two rows beneath it
Copy the last value above the first blank row and offset down one row into the first new inserted blank row and paste it.
Now I managed to find this example of something similar to what I'm trying to do at the basic level (hence my question here):
Insert row between different data and it's from that that I got this:
function doSomething() {
var spreadsheet = SpreadsheetApp.openById('mySheetID'),
tab = spreadsheet.getSheets()[0], // Get the first tab
values = tab.getRange(2, 1, tab.getLastRow(), 3).getDisplayValues(), //Get the values beginning in the second row because of the headers
newValues = [], // Empty array where we're gonna push the new values
lastName;
// Loop through all the values
for(var i = 0; i <values.length; i++){
// If the last name is different from the current name or the last date is different from the current date add an empty "row" to the new array
if((lastName != undefined) && (values[i][1] != lastName)){
newValues.push(['','','']);
}
//Add the current row to the new array
newValues.push(values[i]);
//Sets the lastDate and lastName to the current values for the next evaluation
lastName = values[i][1];
}
//Sets the new values
tab.getRange(2,1,newValues.length,3).setValues(newValues)
},
This does insert a new row but at every other row so I suspect that it's picking up on data that's in Column C (which is different on every row) as opposed to the data in Column A.
I've Googled a lot to find out even how to open a sheet with the SpreadsheetApp.openById bit and so I'm asking for help or if you can point out bits of the above code to look into. I know it's been very helpfully commented but as I said I'm completely new to javascript.
If it helps there is a an Excel/VBA script that works perfectly:
Sub InsertRowsAtValueChange()
Dim i As Long
Application.ScreenUpdating = False
For i = Range("A" & Rows.Count).End(xlUp).Row To 2 Step -1
If Range("A" & i - 1).Value <> Range("A" & i).Value Then
Rows(i).Resize(2).Insert
Range("M" & i).Value = Range("A" & i - 1).Value
End If
Next
Application.ScreenUpdating = True
End Sub
They're some diagrams in the below VBA question but can't see how to do that here, sorry: https://www.mrexcel.com/forum/excel-questions/1034334-insert-blank-row-offset-copy-paste.html#post4964822
Thanks for your time and any help, I have no experience with javascript, sorry.
Max.
QUICK UPDATE 09/12/17:
I've managed to get parts one and two off of my "Wish List" completed now. I don't fully understand it but I've commented where I can to remind myself later:
function importFromSDE() {
// Open the Google Sheet
var spreadsheet = SpreadsheetApp.openById('mySheetID'),
// Get the tab by name
tab = spreadsheet.getSheetByName('Testing'),
// Get the values beginning in the second row because of the headers
// Values from Row 2, Column 1, Down to last row, across 11 columns
values = tab.getRange(2,1,tab.getLastRow(),11).getDisplayValues(),
// Empty array where we're gonna push the new values
newValues = [],
lastT1Item;
// Loop through all the values
for(var i=0; i<values.length; i++){
// If the last name is different from the current name or the last date is different from the current date add an empty "row" to the new array
if((lastT1Item != undefined) && (values[i][0] != lastT1Item)){
newValues.push(['','','','','','','','','','','']);
newValues.push(['','','','','','','','','','','']);
}
//Add the current row to the new array
newValues.push(values[i]);
//Sets the lastT1Item to the current values for the next evaluation
lastT1Item = values[i][0];
}
//Sets the new values
tab.getRange(2,1,newValues.length,11).setValues(newValues)
}
I am trying to loop through rows within a spreadsheet and identify if a particular row has the key word "hello" and move that entire row into a new spreadsheet.
I have attempted the following code. The code works for the first row but doesn't loop through and stops after the first row. Expanding the range selection to "C1:E32" does not help.
function Edit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activatedSheetName = ss.getActiveSheet().getName();
var ActiveSheet = ss.getSheetByName("ActiveSheet"); // source sheet
var MoveDatatoThisSheet = ss.getSheetByName("MoveDatatoThisSheet"); // target sheet
var re = new RegExp(/(Hello)/i);
var startRow = 1;
var endRow = ss.getLastRow();
var getRange = ss.getDataRange();
var getRow = getRange.getRow();
for (var ree = startRow; ree <= endRow; ree++) {
// if the value in column D is "Approved", move the row to target sheet
cellValue = ss.getRange("C1:E1");
if (cellValue.getValue().match(re)) {
// insert a new row at the second row of the target sheet
MoveDatatoThisSheet.insertRows(2, 1);
// move the entire source row to the second row of target sheet
var rangeToMove = ActiveSheet.getRange(/*startRow*/ getRow, /*startColumn*/ 1, /*numRows*/ 1, /*numColumns*/ ActiveSheet.getMaxColumns());
rangeToMove.moveTo(MoveDatatoThisSheet.getRange("A2"));
// add date and time of when approved to target row in column E
MoveDatatoThisSheet.getRange("E2").setValue(Date());
// delete row from source sheet
ActiveSheet.deleteRow(cellValue, 1);
}
}
}
Your loop never uses the variable ree, it only operates with cellValue = ss.getRange("C1:E1").
Another problem is that deletion shifts the rows under the deleted one, possibly causing subsequent operations to act on a wrong row. When you go through an array of rows, deleting some of them, do it bottom up, not top down.
for (var ree = endRow; ree >= startRow; ree--) {
var rangeToCheck = ss.getRange(ree, 3, 1, 3); // 3 columns starting with column 3, so C-E range
if (rangeToCheck.getValues()[0].join().match(re)) { // joining values before checking the expression
MoveDatatoThisSheet.insertRows(2,1);
var rangeToMove = ActiveSheet.getRange(/*startRow*/ getRow, /*startColumn*/ 1, /*numRows*/ 1, /*numColumns*/ ActiveSheet.getMaxColumns());
rangeToMove.moveTo(MoveDatatoThisSheet.getRange("A2"));
// add date and time of when approved to target row in column E
MoveDatatoThisSheet.getRange("E2").setValue(Date());
// delete row from source sheet
ActiveSheet.deleteRow(ree);
}
}
If the goal is to check only column D (say), the code simplifies slightly
var rangeToCheck = ss.getRange(ree, 4); // column D in row ree
if (rangeToCheck.getValue().match(re)) { // joining values before checking the expression
Performance
As Google recommends, one should avoid multiple calls to getValues / setValues and such, instead grabbing all necessary data at once, processing it, and making batch changes at once. E.g., instead of placing it a row in another sheet, add it to an array; when the loop ends, place the entire array in that sheet.
I am trying to write a function that will iterate over each row in a master spreadsheet and check the value of the 3rd column against every row in a second spreadsheet and compare it with the value in the 6th column, then change the value in the 4th column of the master spreadsheet based on the result. The script is a standalone script because its part of a larger project.
The problem is that when I try to set the value of the 4th column in the spreadsheet, I am getting an error that reads "Cannot find function setValue in object SENT". SENT is the value that is in that cell, and I do not understand how it became an object or how I would need to change to code to get at the value to change it. I checked this post about someone having a problem not being able to act on a date value, but I could not figure out how to take that feedback and apply it to this problem.
function eFormCheck() {
var masterSS = SpreadsheetApp.openById("mastersheet");
var masterSheet = masterSS.getSheets()[0];
var masterRange = masterSheet.getDataRange();
var masterData = masterRange.getValues();
var workersCompSent = "SENT";
var workersCompRec = "RECEIVED";
var workersCompSS = SpreadsheetApp.openById("formsheet");
var wcSheets = workersCompSS.getSheets()[0];
var wcRange = wcSheets.getDataRange();
var wcData = wcRange.getValues();
for (var i = 1; i < masterData.length; i++) {
var row = masterData[i];
var email = row[2];
var rowLog = row[3]; //on my spreadsheet this value is "SENT"
for (var j = 1; j < wcData.length; j++) {
var wcRow = wcData[j];
var wcEmail = wcRow[5];
if (email == wcEmail) {
rowLog.setValue(workersCompRec);//having an issue with the value of the third column being an object, but I don't understand how its an object
}
}
}
}
The setValue() method only works on the Range Class. You can't chain the setValue() method to a variable or a cell value. You must first define a range. There are 4 variations of the getRange() method. If you want to set a value in a single cell, then you must get a range that is a single cell. You may need to use your variable i in the getRange() method to get the row. If the column is always the same, then you can "hard code" the column value.
sheet.getRange(i, 4).setValue(workersCompRec);