Synchronizing function calls on google app script - javascript

I'm working with a sheet that has around 700 rows, and each row has a phrase on the first Column.
I'm trying to write a script, that by given a sheet similar to the one I have - would add an additional column to the right with the number of words in this row and after adding the counting on each row, it will sort this sheet by descending order based on the added column.
What happens, is that apparently there's asynchronicity, it first sorts by the second column and only then adds the counting... I looked around and couldn't find a decent solution to this problem... any ideas?
The code:
function sortByNumOfKeywords(lang, col, sheet) {
var file = findFileByName("Translate Keywords List");
var spreadsheet = SpreadsheetApp.openById(file.getId());
//Getting the sheet name from the list above
var sheet = findSheetByName(spreadsheet,'Spanish');
//Getting Table bounds (Two-Dimensional array);
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
//add one column (Column H) to hold the number of words of each keyword in the first column
addWordsCounterCol(sheet, col, lastRow, lastColumn);
//sorting by the added column(H)
sheet.sort(8, false);
}
//sets a new column at the end of the table with the number of keywords of each cell in the given Row
function addWordsCounterCol(sheet, col, lastRow, lastCol){
sheet.activate();
var colChar = String.fromCharCode(65 + col-1); //Converts the Col number to it cohherent Big letter
var formulas = []; //Formulas will be set as a two-dimensional array
for(var i=2; i<lastRow+1; i++){
var cell = sheet.getRange("" +colChar + i);
var numOfKeys = "=COUNTA(SPLIT(trim("+ colChar + i + "), \" \"))";
formulas[i-2] = []; //initializing row with a new array
formulas[i-2].push(""+numOfKeys); // each row has only one column, with this specific String
}
var cells = sheet.getRange("H2:H"+(lastRow));
cells.setFormulas(formulas);
}

Try use flush(). Applies all pending Spreadsheet changes.
It works fine for me in your code:
...
SpreadsheetApp.flush();
//sorting by the added column(H)
sheet.sort(8, false);
...

Related

Google Apps Script - If Last Column in table, then paste data with new row

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:

How to copy values from one sheet and paste them into another using Google Sheets Macros?

I'm writing a Google Sheets Macros without having a lot of knowledge about syntax.
What I want to do is the following:
I want to copy the values which are matching in a source matrix into another table. However, I don't know how to write that as a Macros.
I've written the following code:
function CalcularCruces() {
var spreadsheet = SpreadsheetApp.getActive();
var sourceSheet = spreadsheet.getSheetByName("Cruces Activo-Amenazas");
var destinationSheet = spreadsheet.getSheetByName("AnĂ¡lisis de Riesgos");
/** Total number of left column values from source table **/
const maxAmenazas = 29;
for(var i = 0; i < maxAmenazas; i++) {
/** Now I need to get the column and row values which are matching with the checkbox
and paste them into another table **/
}
};
Here is an example of the input table and how the output table should look like after executing the macros.
Input Table Sheet
Output Table Sheet
Edit:
I need the data to be written next to this static columns:
Actual Output
Desired Output
You can do the following:
Retrieve the data from the source sheet via getDataRange and getValues.
For each row in this data (excluding the headers row, that has been retrieved and removed from the array with shift), check which columns have the checkbox marked.
If the corresponding checkbox is marked, write the corresponding values to the destination sheet with setValues.
It could be something like this:
function CalcularCruces() {
var spreadsheet = SpreadsheetApp.getActive();
var sourceSheet = spreadsheet.getSheetByName("Cruces Activo-Amenazas");
var destinationSheet = spreadsheet.getSheetByName("AnĂ¡lisis de Riesgos");
destinationSheet.getRange("A2:B").clearContent();
var values = sourceSheet.getDataRange().getValues(); // 2D array with all data from source sheet
var headers = values.shift(); // Remove and retrieve the headers row
for (var i = 1; i < values[0].length; i++) { // Iterate through each column
for (var j = 0; j < values.length; j++) { // Iterate through each row
var activo = values[j][0]; // Activo corresponding to this row
if (values[j][i]) { // Check that checkbox is marked
// Get the row index to write to (first row in which column A and B are empty):
var firstRow = 2;
var firstCol = 1;
var numRows = destinationSheet.getLastRow() - firstRow + 1;
var numCols = 2;
var firstEmptyRow = destinationSheet.getRange(firstRow, firstCol, numRows, numCols).getValues().filter(function(row) {
return row[0] !== "" && row[1] !== "";
}).length + firstRow;
// Write data to first row with empty columns A/B:
destinationSheet.getRange(firstEmptyRow, firstCol, 1, numCols).setValues([[headers[i], activo]]);
}
}
}
};
Notes:
All data is added to the target sheet every time the script is run, and this can lead to duplicate rows. If you want to avoid that, you can use clearContent at the beginning of your script, after declaring destinationSheet, to remove all previous content (headers excluded):
destinationSheet.getRange("A2:B").clearContent();
In this sample, the number of amenazas is not hard-coded, but it dynamically gets the number of rows in the source sheet with getValues().length. I'm assuming that's a good outcome for you.
UPDATE: Since you have other columns in your target sheet, you cannot use appendRow but setValues. First, you have to find the index of the first row in which columns A and B are empty. This is achieved with filtering the array of values in columns A-B and filtering out the elements in which the two values are empty (with filter).
Reference:
Sheet.getDataRange
Range.getValues
Array.prototype.shift()
Sheet.appendRow(rowContents)
Array.prototype.filter()
Range.clearContent()

How to paste values from one sheet to another to last row of specific column

A novice on app scripts, but managed to successfully build my own script through much research, but now my script is running into errors. Below is my current code:
function MyFunction() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Daily Sales');
var range = ss.getRange('B8:H83');
var data = range.getValues();
var OUrange = ss.getRange('K8:Q83');
var OUdata = OUrange.getValues();
var ts = sss.getSheetByName('Tracker');
ts.getRange(ts.getLastRow()+1, 1,data.length, data[0].length).setValues(data);
ts.getRange(ts.getLastRow()+1, 1,OUdata.length, OUdata[0].length).setValues(OUdata);
}
In the Daily Sales sheet I am copying values from columns B-H and K-Q and pasting them in the last row of the Tracker sheet starting at Column A. The Daily Sales values in Col. K-Q are pasted correctly below the B-H values, so happy with those results.
On the Tracker sheet these values are in Columns A-G. However I have since added formulas in Columns I and J based on the script data and a manual entry in Column H. These formulas are copied down the entire column within the sheet (i.e. row 5000). Since adding these formulas, the script is now pasting values in A5001.
I realize it is because of these formulas, but is there a way to update my script to paste in the last row of column A while maintaining those formulas in Columns I and J? Thanks in advance.
You could create a helper function that computes the first free row of a given column (NOT a column with formulas)
function MyFunction() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Daily Sales');
var range = ss.getRange('B8:H83');
var data = range.getValues();
var OUrange = ss.getRange('K8:Q83');
var OUdata = OUrange.getValues();
var ts = sss.getSheetByName('Tracker');
var values = data.concat(OUdata).filter(function (r) {return r[0]});
var fr = findFirstFreeRow(ts, 1) // second parameter represents column to check for first free row
ts.getRange(fr, 1,values.length, values[0].length).setValues(values);
}
function findFirstFreeRow(sheet, colNum) {
var v = sheet.getRange(1, colNum, sheet.getLastRow()).getValues(),
l = v.length,
r;
while (l >= 0) {
if (v[l] && v[l][0].toString().length > 0) {
r = (l + 2);
break;
} else {
l--;
}
}
return r;
}
See if that helps?
After deleting the extra empty rows from your 'Tracker' sheet, you can use the appendRow function to create a new row with the manual values (leaving the cells that need formulas blank).
After the manual values are in, you can then get the cells that need formulas and use setFormula on them.
For the sake of brevity, if you wanted column A,B,C,E,F to have manual values and column D to have a formula you would do:
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(['A','B','C','','E','F']);
sheet.getRange(sheet.getLastRow(),4).setFormula('=FORMULA(HERE)');

Shifting data to first available cell (not row) in a column

I have a sheet to monitor inventory. I have a script in the sheet to copy values of rows with data and paste to the first available row on another sheet and then clear the content. This leaves holes in my data on the original sheet. I want to consolidate all my data up to the first available cells in a column so there would never be empty rows between rows with data. Unfortunately, I can't do the same thing I do with the other sheet, as I have certain columns in my row which have formulas that need to remain (Sum formulas to figure weight, etc). If I grab the entire row, it messes up my formulas or the formulas mess up the script (row is not available because it contains a value). So basically what I have is this:
Sheet1 name = 'CURRENT MONTH'
Sheet2 name = 'SHIPPED INVENTORY'
Total Range with data on Sheet1 = A11:W61
Ranges with data that needs to shift up = A11:O61, R11:S61, V11:W61
Ranges with formulas that cannot shift = P11:Q61, T11:U61
I've searched what I could but either I cannot word my dilemma correctly or the answers are beyond my skill level and I can't make them work for me. Any assistance is greatly appreciated!
Without seeing how you are moving your information from one sheet to another, this code copies the entire row to the first row of the inventory sheet:
function myFunction ()
{
...
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var month_sheet = ss.getSheetByName ("CURRENT MONTH");
var shipped_sheet = ss.getSheetByName ("SHIPPED INVENTORY");
var month_values = month_sheet.getDataRange ().getValues ();
for (var i = 11; i < month_values.length && i <= 61; i++)
{
if (test_value) // This is a row you want to move to the other sheet
{
// Create a row in the SHIPPED INVENTORY sheet,
// and copy the full row into it
shipped_sheet.insertRowAfter (1); // Assuming a header row
var shipped_row_1= shipped_sheet.getRange (2, 1); // Get second row in file
var month_range = month_sheet.getRange ((i + 1), 1, 1, month_sheet.getMaxColumns ());
month_range.copyTo (shipped_row_1);
// Now move everything else up
moveRangeUp (month_sheet, "A", i, "O61");
moveRangeUp (month_sheet, "R", i, "S61");
moveRangeUp (month_sheet, "V", i, "W61");
i--; // Since everything moved up, check this row's new information
}
}
...
}
function moveRangeUp (sheet, first_col, row, last_col_row)
{
var from_a1Notation = first_col + (i + 1) + ":" + last_col_row;
var to_a1Notation = first_col + (i);
var copy_range = sheet.getRange (from_a1Notation);
var to_range = sheet.getRange (to_a1Notation);
copy_range.copyTo (to_range);
// After shift up, delete last row of data, since it is also moved up
// Always assuming that the last row is row 61
var delete_a1Notation (first_col + "61:" + last_col_row);
var delete_range = sheet.getRange (delete_a1Notation);
var delete_range.clear ();
}

Google app script - looping through the rows in a spreadsheet

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.

Categories

Resources