How to 'For Loop' through Google Documents table - javascript

I have a table in a Google Document (Not Sheet). The first column contains an ID number which is a match to its relative ID in the Google Sheet that the Data was submitted from.
//This is what the table looks like
//[id][name][favourite cheese]
//[3 ][bob ][chedder]
//[4 ][jane][old english]
I need to
Loop through each row in the Google DOCUMENT table.
Identify if the cell in the first column contains 'text' (Identical to ID number)
If it contains the text update the row with new data
Here is my current code:
// Grab the Table
var body = DocumentApp.openById('theId').getBody(),
searchElement = body.findElement(DocumentApp.ElementType.TABLE),
element = searchElement.getElement(),
table = element.asTable();
That section of code is utilized to grab the table since you can't name tables in Google Documents.
I am so surprised I cannot find more info. I'm doing my best to utilize the documentation and have a feeling I will be using a 'For Loop' to search each row but will I need to .getElement(row) to loop through? Could I use .findText() or would that bring up every part of the table that contains the text. Maybe I could loop the .getElement(row) and .findText() in the first column of each row somehow?
I know looping is a fairly basic Javascript concept it's just the Google Documents way of doing things is confusing me.

You are correct, you can loop through the rows using a Javascript for loop.
for (var i = 0; i < table.getNumRows(); ++i) {
var row = table.getRow(i);
Logger.log(row.getText());
}
Once you have the row, you can work with it how you like, for example getting the text from the first cell:
row.getCell(0).getText()
A string comparison in JavaScript can be a straightforward ===, and then you can edit the text of the cell you are targeting (in this example, second column):
if (row.getCell(0).getText() === id) {
row.getCell(1).setText('foo');
}

for (let r = 0 ; r < table.getNumRows(); r++) {
row = table.getRow(r);
for(let c = 0; c < row.getNumChildren(); c++){
Logger.log(table.getCell(r,c).getText());
}
}
for "full" for loop, you can utilize the row and call the number of its children...

Related

How to validate a selectors without running the entire test from beginning

I was wonder if someone found an easy way to verify a selector work properly with the TestCafe syntax without running the entire test over and over?
E.g:
I want to verify that the last table column cells has all the values in a array I provide:
in order to do I try to detect the last column but for validation, I have to execute the entire testcafe test from start.
How do you recommend approaching this scenario?
The following snippet I wrote to verify I catch all the right column cells, but it took me a while because each time I had to run the test again.
console.log(`total rows: ${await (Selector('tbody > tr').find('td:nth-of-type(3)')).count}`);
Another thing, how I save to array the most right column cells?
Source:
Stay tuned to Selector Debug Panel feature.
As for your 'save to array the most right column cells' question, selector provides methods and properties to select elements on the page and get their state, but has no 'rows' and 'columns' properties.
So, you can use the following approach to iterate over your table cells:
const table = Selector('#table');
const rowCount = await table.find('tr').count;
const columnCount = await table.find('tr').nth(0).find('td').count;
for(let i = 0; i < rowCount; i++) {
for(let j = 0; j < columnCount; j++) {
let tdText = await table.find('tr').nth(i).find('td').nth(j).textContent;
}
}
and add required cells to an array inside the loop

Trying to compare cell values for two row in Google Spreadsheet and increment count if they are equal

First time building a custom function for google sheets. I'm trying to compare strings in each cell for two rows and incrementing the value of a cell in a Point column for each cell that is equal. I have the following function done in the script editor:
function tally(a,b) {
var tally = 0;
for( var i = 0; i < a.length; i++){
if( a[i] == b[i]){
tally ++;
}
}
return tally;
}
But it doesn't seem to work, am I doing something wrong? Appreciate it!
That function is comparing each cell to the cell directly next to it, linearly. Is that what you're looking to do? If you want to compare, for example, the first cell in column A to ever cell in column B, and so on for every cell, you need to use nested 'for' loops.
If this isn't your problem, can you be more specific about your error?
Hey guys I figured it out using the following formula:
=SUMPRODUCT(COUNTIF(range1,range2))

Sort data in google sheets (with custom sort order) using google scripts

As the title says, I need to sort my data depending on my first column which contains som rank names. I do not wan't it sorted alphabetically but rather in a order I define myself.
Example:
Admin_contact
Commissioner
Battalion Chief
Paramedic
I want them listed in that order using google scripts so it automatically sorts when data gets edited.
Hope someone can help me achieve this
Absolutely, you can create a google script function onEdit() in the spreadsheet which will fire each time you edit something.
There are a few ways to do it. One of the most obvious ones (probably not the best as it loops through the data a lot):
Using getLastColumn() and getLastRow() grab all the data. You then use 2 arrays. 1 is current and 1 is sorted.
Loop through the current array and if data[i][0] matches the rank, output it to the sorted array
After you finish one loop, change to the rank you expect next (keep them in an array and you can use a for loop with rank[n])
Loop the array until you run out of the ranks.lenght
So it would be something along the lines of
function onEdit()
//Get the data
rank = [rank1, rank2, rank3]
for (n=0 ; n < rank.lenght; n++) {
for (i = 0; i < data.lenght; i++)
if (data[i][0] === rank[n]) {
sorted[x] = data[i];
x++;
}
}
}
Though I would recommend to make it so you can manually run it instead of an onEdit. Or you can set it so only if the last row is edited, then to fire it (make a return at the start of the script if the edited row is less than getLastRow()+1
Try this code:
function customSort(array, arrayOrder, numColumn) {
var order = [];
// transpose vertical 2d array into 1d array
for (var i = 0; i < arrayOrder.length; i++) {
order.push(arrayOrder[i][0]);
}
// sort
return array.sort(function (a, b) {
return order.indexOf(a[numColumn-1]) - order.indexOf(b[numColumn-1]);
});
}
Here's the use like custom function:
Same example for column 2:

Is it possible to hide many rows of a Google spreadsheet at once?

So I'm using this script below (credit: Eduardo Kuwakino).
The problem I have that it hides the rows from column A:A with the value "x" one by one.
Is it possible to make this "less painful" for the people who are editing these spreadsheets?
function hideCells(sheet)
{
var maxRows = sheet.getMaxRows();
//show all the rows
sheet.showRows(1, maxRows);
//get data from clumn A
var data = sheet.getRange('A:A').getValues();
//iterate over all rows
for(var i=0; i< data.length; i++){
//compare first character, if asterisk, then hide row
if(data[i][0].charAt(0) == 'x'){
sheet.hideRows(i+1);
}
}}
function onOpen() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i=0; i< sheets.length; i++){
hideCells(sheets[i]);
}
}
There is a method hideRows which can hide several rows at once, but they must be consecutive. In your situation, the rows will not be consecutive in general. So script has to hide them one by one (or maybe in groups when a few happen to be together, but I don't think this will help much). And indeed, this will be slow in a large sheet.
As an alternative, I suggest using a filter view, specifically filter the sheet by the column A with custom formula =A2<>"x"
Each filter view has its own URL, which can be sent to people editing the spreadsheet. They will see only the rows where the value in A is different from "x".

indexOf returning -1 despite object being in the array - Javascript in Google Spreadsheet Scripts

I am writing a script for a Google Docs Spreadsheet to read a list of directors and add them to an array if they do not already appear within it.
However, I cannot seem to get indexOf to return anything other than -1 for elements that are contained within the array.
Can anyone tell me what I am doing wrong? Or point me to an easier way of doing this?
This is my script:
function readRows() {
var column = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("Director");
var values = column.getValues();
var numRows = column.getNumRows();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var directors = new Array();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (directors.indexOf(row) == -1) {
directors.push(row);
} else {
directors.splice(directors.indexOf(row), 1, row);
}
}
for (var i = 2; i < directors.length; i++) {
var cell = sheet.getRange("F" + [i]);
cell.setValue(directors[i]);
}
};
When you retrieve values in Google Apps Script with getValues(), you will always be dealing with a 2D Javascript array (indexed by row then column), even if the range in question is one column wide. So in your particular case, and extending +RobG's example, your values array will actually look something like this:
[['fred'], ['sam'], ['sam'], ['fred']]
So you would need to change
var row = values[i];
to
var row = values[i][0];
As an aside, it might be worth noting that you can use a spreadsheet function native to Sheets to achieve this (typed directly into a spreadsheet cell):
=UNIQUE(Director)
This will update dynamically as the contents of the range named Director changes. That being said, there may well be a good reason that you wanted to use Google Apps Script for this.
It sounds like an issue with GAS and not the JS. I have always had trouble with getValues(). Even though the documentation says that it is a two dimensional array, you can't compare with it like you would expect to. Although if you use an indexing statement like values[0][1] you will get a basic data type. The solution (I hope there is a better way) is to force that object into a String() and then split() it back into an array that you can use.
Here is the code that I would use:
var column = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("Director");
var values = column.getValues();
values = String(values).split(",");
var myIndex = values.indexOf(myDirector);
If myDirector is in values you will get a number != -1. However, commas in your data will cause problems. And this will only work with 1D arrays.
In your case: var row = values[i]; row is an object and not the string that you want to compare. Convert all of your values to an array like I have above and your comparison operators should work. (try printing row to the console to see what it says: Logger.log(row))
I ran into a similar problem with a spreadsheet function that took a range as an object. In my case, I was wanting to do a simple search for a fixed set of values (in another array).
The problem is, your "column" variable doesn't contain a column -- it contains a 2D array. Therefore, each value is it's own row (itself an array).
I know I could accomplish the following example using the existing function in the spreadsheet, but this is a decent demo of dealing with the 2D array to search for a value:
function flatten(range) {
var results = [];
var row, column;
for(row = 0; row < range.length; row++) {
for(column = 0; column < range[row].length; column++) {
results.push(range[row][column]);
}
}
return results;
}
function getIndex(range, value) {
return flatten(range).indexOf(value);
}
So, since I wanted to simply search the entire range for the existance of a value, I just flattened it into a single array. If you really are dealing with 2D ranges, then this type of flattening and grabbing the index may not be very useful. In my case, I was looking through a column to find the intersection of two sets.
Because we are working with a 2D array, 2dArray.indexOf("Search Term") must have a whole 1D array as the search term. If we want to search for a single cell value within that array, we must specify which row we want to look in.
This means we use 2dArray[0].indexOf("Search Term") if our search term is not an array. Doing this specifies that we want to look in the first "row" in the array.
If we were looking at a 3x3 cell range and we wanted to search the third row we would use 2dArray[2].indexOf("Search Term")
The script below gets the current row in the spreadsheet and turns it into an array. It then uses the indexOf() method to search that row for "Search Term"
//This function puts the specified row into an array.
//var getRowAsArray = function(theRow)
function getRowAsArray()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Get the current spreadsheet
var theSheet = ss.getActiveSheet(); // Get the current working sheet
var theRow = getCurrentRow(); // Get the row to be exported
var theLastColumn = theSheet.getLastColumn(); //Find the last column in the sheet.
var dataRange = theSheet.getRange(theRow, 1, 1, theLastColumn); //Select the range
var data = dataRange.getValues(); //Put the whole range into an array
Logger.log(data); //Put the data into the log for checking
Logger.log(data[0].indexOf("Search Term")); //2D array so it's necessary to specify which 1D array you want to search in.
//We are only working with one row so we specify the first array value,
//which contains all the data from our row
}
If someone comes across this post you may want to consider using the library below. It looks like it will work for me. I was getting '-1' return even when trying the examples provide (thanks for the suggestions!).
After adding the Array Lib (version 13), and using the find() function, I got the correct row!
This is the project key I used: MOHgh9lncF2UxY-NXF58v3eVJ5jnXUK_T
And the references:
https://sites.google.com/site/scriptsexamples/custom-methods/2d-arrays-library#TOC-Using
https://script.google.com/macros/library/d/MOHgh9lncF2UxY-NXF58v3eVJ5jnXUK_T/13
Hopefully this will help someone else also.
I had a similar issue. getValues() seems to be the issue. All other methods were giving me an indexOf = -1
I used the split method, and performed the indexOf on the new array created. It works!
var col_index = 1;
var indents_column = main_db.getRange(1,col_index,main_db.getLastRow(),1).getValues();
var values = String(indents_column).split(","); // flattening the getValues() result
var indent_row_in_main_db = values.indexOf(indent_to_edit) + 1; // this worked
I ran into the same thing when I was using
let foo = Sheet.getRange(firstRow, dataCol, maxRow).getValues();
as I was expecting foo to be a one dimensional array. On research for the cause of the apparently weird behavior of GAS I found this question and the explanation for the always two dimensional result. But I came up with a more simple solution to that, which works fine for me:
let foo = Sheet.getRange(firstRow, dataCol, maxRow).getValues().flat();

Categories

Resources