I currently have code that searches through a single Google sheet for indexOf a number, and assigns the row number of a match to an variable. Here's an example:
for(i=0;i<values.length; i++){
itemvalue = values[i][column1-1];
codevalue = values[i][column2-1];
if(itemvalue.indexOf("5019")>-1){
row = i+1; itemcode = 5019; indexes.push(i+1)
}
}
Unfortunately the sheet contains multiple rows of strings that contain "5019". So I'm looking for a way for indexOf to continue searching and assign a new variable for the row number of each match. So if "5019" was found at row 50,51, and 54, then row,row1,row2 will be assigned to each row number.
Appreciate any help on this, also please let me know if I missed providing any information.
Edit:Thanks to the link provided by daniel, I was able to get all the matching row numbers into an array. But I'm still trying to figure out how to assign a variable to each value in the array so I can get the value of the cells that reside at the rows and add them together.
I've found the solution I sought for. Here's what I did:
for(i=0;i<values.length; i++){
itemvalue = values[i][column1-1];
codevalue = values[i][column2-1];
if(itemvalue.indexOf("5019")>-1){
row = i+1; itemcode = 5019; indexes.push(i+1)
}
for(j=k=0;j<indexes.length; j++){
rownumb.push(ss.getRange(indexes[j],qtycol).getValue()); //Get value of the cells using the row numbers from the indexes array
}
for(l=0;l<rownumb.length; l++){
rowtotal += rownumb[l]; //Adds all the values together
}
}
So I made a for loop to get the value of cells in the rows that was in the indexes array, then made a new array called "rownumb" that holds the cell values. I then made another for loop to add all the values in array rownumb together and got exactly what I was looking for.
Related
I am a non-programmer and quite new to Office Scripts and I would love some help.
I am currently trying to delete entire rows if the cell in the "Change Flag" column, which happens to be the second column on the Excel sheet, contains the word "Delete" or "Deleted".
I managed to delete rows if they contain the word "Delete" with the following script but could not make the script cells with "Deleted" too. I tried throwing brackets in there but it unfortunately did not work.
Can someone suggest a best practice to handle the deletion of rows based on multiple text matches (delete if x = y OR z)?
function main(workbook: ExcelScript.Workbook) {
// Get the used range on the current sheet.
const currentSheet = workbook.getActiveWorksheet();
let table = workbook.getTables()[0];
// Get the RangeAreas object for each cell with a formula.
const usedRange = currentSheet.getUsedRange();
//get the values of the range
let values = usedRange.getValues();
//get the row count of the range
let rowCount = usedRange.getRowCount();
//starting at the last row, check if the cell in column '1' equals to 'Delete'. If it is, then delete the entire row.
for (let i = rowCount - 1; i >= 0; i--) {
if (values[i][1] == "Delete") {
usedRange.getCell(i, 1).getEntireRow().delete(ExcelScript.DeleteShiftDirection.up)
}
}
}
Your if statement should look like the below:
if (values[i][1] == "Delete" || values[i][1] == "Deleted")
If you are looking for more general tutorials, here is a page I found that explains if statements a little more: https://www.w3schools.com/jsref/jsref_if.asp
You can use the includes() method of the string object to do this. To use that method, you'd have to cast the value to a string using the toString() method like so:
if (values[i][1].toString().includes("Delete"))
Using includes, it will flag for both Delete and Deleted.
The words Delete and Deleted may be spelled in a variety of different ways (e.g. delete and deleted, DELETE and DELETED, etc.). Those different spellings will not be flagged by includes(). If want those different spellings to be evaluated in the same way, you can use a method like toLowercase() to do that. After calling that method, you'd just provide the includes() method with a lowercase version of the spelling like so:
if (values[i][1].toString().toLowerCase().includes("delete"))
This approach will also flag for any other text that includes delete. So if you have text in a cell like "this row should not be deleted" this code will flag that text as well.
I have working code that takes data from two non-adjacent columns in a Google Spreadsheet, looks for unique values in the first column, and if unique creates a new array with the unique value from the first column and corresponding value in the second column. The problem is, the data I am using is already somewhat long (413 rows) and will only get longer over time. It takes about 1-2 minutes for the code to run through it. I've been looking for a shorter way to do this and I've come across the filter() and map() array functions which are supposedly faster than a for loop but I can't get them implemented correctly. Any help with these or a faster method would be greatly appreciated. The code I have right now is below.
function getkhanassignments(rows) {
var assignmentsraw = [];
var temparray = [];
var previousassignment = datasheet.getRange(50,1).getValue();
for(i=0, j=0;i<rows-1;i++) {
if(datasheet.getRange(50+i,1).getValue() != previousassignment) {
previousassignment = datasheet.getRange(50+i,1).getValue();
assignmentsraw[j] = new Array(2);
assignmentsraw[j][0] = datasheet.getRange(50+i,1).getValue();
assignmentsraw[j][1] = datasheet.getRange(50+i,8).getValue();
j++;
}
}
Logger.log(assignmentsraw);
return assignmentsraw;
}
The answers I've found elsewhere involve just getting unique values from a 1d array whereas I need unique values from a 1d combine with corresponding values from another 1d array. The output should be a 2d array with unique values from the first column and their corresponding values in the second column.
Solution:
The best practice of looping through ranges in Google Apps Script is to dump the range values into a 2D array, loop through that array, and then return the output array back to Google Sheets.
This way, there would be no calls to Sheets API inside loops.
Sample Code:
function getkhanassignments(rows) {
var assignmentsraw = [];
var table1 = datasheet.getRange(50,1,rows).getValues();
var table2 = datasheet.getRange(50,8,rows).getValues();
var previousassignment = table1[0][0];
assignmentsraw.push([table1[0][0],table2[0][0]]);
for(i=0; i<rows; i++) {
if (table1[i][0] != previousassignment) {
assignmentsraw.push([table1[i][0],table2[i][0]]);
previousassignment = table1[i][0];
}
}
Logger.log(assignmentsraw);
return assignmentsraw;
}
References:
Class Range
push()
I want to import rows from one google sheet to the other, however source sheet imports a number of empty rows. Now I use a filter function to get rid of these rows but they will not disappear, can anyone tell me why?
var a = SpreadsheetApp.openByUrl("url").getSheetByName("Admin Use Only").getRange(4,1,6,21).getValues();
var b = SpreadsheetApp.getActive().getSheetByName('Credit_Detail');
b.getRange(b.getLastRow() +1, 1, a.length,21).setValues(a);
//filter function below:
var otarget=b.getRange(2,1,b.getLastRow()-1, 26).getValues();
var data=otarget.filter(function(r){
return !r.every(function(cell){
return cell === "";});
});
Logger.log(data);
b.getRange("A2:Z").clearContent();
b.getRange(3,1,data.length,data[0].length).setValues(data);
here's how I would do it. First, create an variable to store the array of the source. then run a for loop scanning the first column for empties. something like: for (var i = 0, i < data.length; i++) { if (data[i][0] != '') { XXXX } }
XXXX means that you can either put a code to create a new set of array which can be passed to the target sheet at once or use append row to transfer non blank rows to the target sheet one by one.
Note: Creating a new array to store non-empty rows would speedup the execution time if you are dealing with large data, thousands of rows.
Google sheets has an option which is selectable from the top menu to separate text into columns when specifying a character. It's possible use a comma or other characters.
I am looking for a script which can do this process automatically for a given column. There are numerous scripts available to do this but I have not been able to accomplish my task using them.
I am using an application on Android which allows me to scan a qr code and send the string of information to Google sheets.
A sample of the information would appear as : 464839|362|2840|927|72|617
I need to separate this information into separate columns when the information is sent to sheets. The process should be automatic.
I have a snip of code which I've found searching however it doesn't work for me.
var range = SpreadsheetApp.getActiveRange();
var cell = range.getCell(1, 1); // Gets the cell A1
var cellValue = cell.getValue(); // Gets the value of the cell A1
var cellArray = cellValue.split("|"); // Splits the cell value by ',' and stores it in array.
//Iterate through the array
for(var i = 0; i < cellArray.length; i++){
Logger.log(cellArray[i]);
}
I'm not very code savvy, please help.
Below would be a code that you place on an installable trigger that runs at a regular interval and iterates through the values of each row in column A and tries to split the value with the pipe symbol and the replace then write those split values along the columns in that row. If this has already been done in the past the function will error when it tries to split the values because no pipe symbol exists but the try/catch will catch that error and allow the function to continue through the loop.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NameOFSheet"); //Change "NameOFSheet to the actual name of the sheet.
var col1Values = sheet.getSheetValues(1, 1, sheet.getLastRow(), 1) //Gets 2d (2 dimensional) array of the Values in Column 1 (getSheetValues(startRow, startColumn, numRows, numColumns)_
var splitVals = []; //Instantiate an empty 1d array variable
//Iterate through the 2d array and set the values
for(var i = 0; i < col1Values.length; i++){
try{
splitVals = col1Values[i][0].split("|"); //Creates a 1d array of values split at every occurrence of the pipe symbol ("|").
//If there is no pipe symbol (which would be the case if this operation has already happened then the array will be blank because .split will throw an error which will get "caught" in the try catch and the row will be skipped
//Iterate over the splitVals array and set the values of the columns along the row (i+1) that you are in.
for(var col = 0; col < splitVals.length; col++){
sheet.getRange(i+1, col+1).setValue(splitVals[col])
}
}catch(e){}
}
}
I commented the code for explanation. I would recommend reading up on 2 dimensional arrays to help you understand them and the code above better.
imagine I have an excel like grid that consists of two parts.
A column and a list.
The column is the columns and the list is the entire collection of the all of the rows.
I can get my columns list with a .length and a .length on the list gives me the number of rows.
So I have my x and my y now.
I want to create an object out of this that would work something like this:
var = ObjList {
row0 = [],
row1 = [],
row2 = [],
column = [],
};
The issue is that the list can be modified in length. The only way i see to approach this is to programmatically create variables. Like so ( psuedo code )
//Variables
var itemName
//Get Column Names
for (var j in columns) { //columns is an object/array
var cName =columns[j].getName(); //I have to get the name otherwise cName just returns as ListItem
columnList.push(cName); //Push cName to my own array so I have an array of strings
}
for (var i in listItems) { //Again working with an object array
item = listItems[i] //Creating an easier reference variable
for (var j = 0; j < columnList.length - 1;j++){ //now I have to use the length of the Column array to find out how wide our list is
itemName = item.getValueByName(columnList[j]); //turning the listitem into a string
row(i).push(itemName); //the is the programmatic variable.
}
}
The problem is I'm not sure where I would declare the variable. Is there some other way for me to loop through the List array and match each index of the listItems array to the index of the column which I am doing here with the getValueByName(columnList[j]).
******************************Edit********************************
An example was requested and I can see how this would be confusing. This is dealing with google sites specifically.
I added comments to the above code as well.
We have a grid.This grid is actually two parts. The column is an object which is why I am creating another array and putting the names into the array. The list is another object. ( LI = lineItem )
|Col1|Col2|Col3|Col4|<~~~ Column Object
|LI |LI |LI |LI |<~~~~~~~~~~~~~~~~~~~|
|LI |LI |LI |LI |<~~~~~~~~~~~~~~~~~~~| List Object
|LI |LI |LI |LI |<~~~~~~~~~~~~~~~~~~~|
WHat I want is to be able to iterate through a loop and break each row into its own array.
I have been able to do that successfully one at a time. But since I would like to have one array per row and the number of rows is dynamic ( because a user can add more rows ) I am confused as to how I would account for that.
I want the entire grid to exist as an object with row properties and column properties that are array. So row0 would be row0[LI1,LI2,LI3,LI4]
Columns[]~~>|Col1|Col2|Col3|Col4| ~~~~~~~|
row0[]~~~~~>|LI1 |LI2 |LI3 |LI4 | ~~~~~~~|
row1[]~~~~~>|LI1 |LI2 |LI3 |LI4 | ~~~~~~~|ObjList
row2[]~~~~~>|LI1 |LI2 |LI3 |LI4 | ~~~~~~~|
Again the width of the grid is
Column.length
and the number of rows can be defined by
itemList.length
So I guess the question is can I have an array of arrays?
So I could have
//Variables
var itemName;
var masterArray = [];
var midArray = [];
//Get Column Names
for (var j in columns) {
var cName =columns[j].getName();
columnList.push(cName);
}
for (var i in listItems) {
item = listItems[i]
for (var j = 0; j < columnList.length - 1;j++){
itemName = item.getValueByName(columnList[j]);
midArray.push(itemName);
if (midArray > columnLisst.length -1) {
masterArray.push(midArray);
midArray.length = 0;
}
}
}
It feels clunky and wrong though.
Yes, you can have a 2 dimensional array in JavaScript. What you have now is an object with another level which is an array, so, in terms of dimensions, there really isn't any difference. Whether it's an object with arrays, or an object with other objects, or an array with other arrays; in all situations you have that second level.
The only issue really is; do you want to be able to access values by name rather than by index? If you want to access values by name, then use an object. Have you considered having an object inside of an object?
I don't see that you are programatically declaring variables. Maybe there is a misunderstanding of the terminology.
In this situation, where there is a second level of data, it would be normal to have nested FOR loops, so even though it's more complex, I don't think it would be considered clunky or wrong.
You'll need to run the code and look at the results to figure out what will work for you. You can use Logger.log("some text: " + aVariableName); statements to print information to the LOG, then VIEW the LOG. Also, view the EXECUTION TRANSCRIPT.
You can also step through lines of code, one line at a time, line by line and view the results.
If you have a specific problem that you can't figure out, provide the error message, and what line of code is producing the error message.