Related
I think this may be a fairly trivial issue but I am still quite new to Node and Javascript.
I have a list and a function as follows:
var my_list = [1, 7, 9, 112, 15, 17, 19, 25, ...]
// Main search function
const search = async(_) => {
// Loop from ID 1 to X
for (let index = 1; index < 10000; index++) {
if (my_index.indexOf(index) !== -1) {
// Try to find a brewery with this ID
const brewery = await findBreweriesById(index);
// If it's found ..
if (brewery) {
console.log(brewery.brewery_name);
}
} else {
console.log(`Nothing found for ID ${index}..`);
}
}
console.log("Finished searching!");
};
search();
The function is going to be running as a call to a rate limited API which means not all values in my_list will be fetched at once. I have to repeat the fetching function. What I want to do is to be able to remove any successfully fetched value from the main list, i.e. my_list and rerun the function until all values in my_list have been fetched.
I know two things,
I need to remove the fetched index element from the list. I have figured out I could do this using my_list.splice(indexOf(index),1)
I know I can use a while loop but I have no idea where exactly I'd put it and what condition to set. This also confuses me as to where exactly then I would be splicing to remove the element.
Any help would be appreciated.
EDIT
To be more specific, the API call is rate limited to 100 calls per minute. After that it returns a 429 Too many requests. So I want to remove all the 200 returns and recycle, i.e. rerun the function for the 429 which should remain in the list.
Maybe try this:
var my_list = [ 1, 7, 9, 112, 15, 17, 19, 25 ]
// Main search function
const search = async _ => {
while( my_list.length > 0 ){
const index = my_list[my_list.length - 1]
// Try to find a brewery with this ID
const brewery = await findBreweriesById(index);
if (brewery) {
my_list.splice(indexOf(index),1)
console.log(brewery.brewery_name);
} else {
console.log(`Nothing found for ID ${index}..`);
}
}
}
search();
I am trying to build a form that can recall data based on a specific number being inputted using Google Scripts and JavaScript. When a number is inputted the JavaScript should call the Google Scripts so that it can locate the values I need based on the number. For example bellow is a google sheets. I want when the user inputs a number it searches for that value in column C and then grabs the data from that row. Ex. if the number is 14 then values: 2021-05-12, 5, 6 and 7 are returned to the JavaScript.
UPDATED:
Everything I want is working however, when I try to retrieve the date from the array in the JavaScript it does not work. The numbers are the only thing functioning. I know the date is in the array as I can see it in the logs.
JavaScript:
function callDataRetriever(){
var number = document.getElementById("number").value;
google.script.run.withSuccessHandler(dataRetriever).retreiveData(number);
}
function dataRetriever(data){
document.getElementById("location").value = data[0]; //This works
document.getElementById("dateOpened").value = data[1]; //This does not work. Stops the function from continuing its task.
document.getElementById("value1").value = data[2]; //Without the date everything here down works
document.getElementById("value2").value = data[3];
document.getElementById("value2").value = data[4];
document.getElementById("value4").value = data[5];
//...
}
Google Scripts (I have 28 total values) Everything here works perfectly as seen in the logs bellow.
function retreiveData(number){
var url = "urlToSpreadsheet";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
var data = ws.getRange(1,1, ws.getLastRow(), ws.getLastColumn()).getValues();
var dataValues = [];
var filterData = data.filter(
function(r){
if(r[2] == number){
var i = 3;
while(i < 29){
dataValues.push(r[i]);
i++;
}
}
}
)
return dataValues;
}
I am not sure if the problem lies in the way the date is formatted.
Thank you!
You can use filter to perform the operation you want
var filterData = data.filter(
function(r){
//Select the index init in 0 in your case is 2
return r[0] == 'YOUR_SEARCH_VALUE'
}
)
// Use your filter data
ui.alert(filterData[0]) //data[row][column]
I believe your goal as follows.
When number is given and run the function retreiveData, you want to search the value of number from the cells "C4:C" of "Data" sheet, and want to retrieve the values of the columns "D", "E" and "F" for the searched row.
In this case, I would like to propose to use TextFinder. When the TextFinder is used, searching value is run in the internal server side. By this, the process cost will be a bit low. Ref
When this is reflected to your script, it becomes as follows.
Modified script:
In this case, please modify retreiveData at Google Apps Script side as follows.
function retreiveData(number){
var url = "linkToSpreadsheet";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
var data = ws.getRange("C4:C" + ws.getLastRow()).createTextFinder(number).matchEntireCell(true).findNext();
if (data) {
return data.offset(0, 1, 1, 4).getValues()[0];
}
return Array(4).fill("No value");
}
And, please modify dataRetriever at Javascript side as follows.
function dataRetriever(data){
console.log(data) // Here, you can check the value from `retreiveData` at the console.
data.forEach((v, i) => {
document.getElementById(`value${i + 1}`).value = v;
});
}
Note:
In this case, when the search value of number is not found, No value is put to the input tags of value1 to value4. When you want to modify this, please modify return Array(4).fill("No value"); for your actual situation.
References:
Benchmark: Process Costs for Searching Values in Spreadsheet using Google Apps Script
Class TextFinder
There are two ways:
var data = [
[1, 55, 5545, 54, 51],
[2, 45, 541, 848, 1215],
[3, 323, 3232, 215, 3051],
[4, 13, 5151, 1513, 2315]
]
number = 3;
// via object where the 'numbers' are keys
var data_obj = Object.fromEntries(data.map(d => [d[0], d.slice(1,)]));
console.log(data_obj[number]); // [ 323, 3232, 215, 3051 ]
// via filter (about the same as #macorreag's answer)
var res = data.filter(d => d[0] == number)[0].slice(1,);
console.log(res); // [ 323, 3232, 215, 3051 ]
An object makes sense in the cases when you need to extract info from the same data several times. For single requests a filter looks better, I think.
And if the numbers always start from 1 and go sequentially 2, 3, 4, 5, ... etc, the simpliest solution is an array and its indexes:
var data = [
[1, 55, 5545, 54, 51],
[2, 45, 541, 848, 1215],
[3, 323, 3232, 215, 3051],
[4, 13, 5151, 1513, 2315]
]
var number = 3;
var data_array = data.map(d => d.slice(1,));
console.log(data_array[number-1]); // [ 323, 3232, 215, 3051 ]
In this case you don't even need the first column 'numbers' at all. You can just extract the next four columns and get the rows by indexes (-1): number 1 is data[0], number 2 is data[1] etc.
I am trying to avoid the following code because it is too slow:
for (var c = 25; c>2; c--){
if (sheet2.getRange(1,c).getValue() == 0)
{sheet2.deleteColumn(c)}
}
Instead I tried to find a list of columns I want to delete from the array and then set the array. (I recently figure out that deleting rows/columns in a loop is very expensive: google script loop performance)
I found this Removing columns of data in javascript array and try to apply it to my code, but it is not working.
Here is the code.
var ary = sheet2.getRange(2,1,outData.length+1,outData[0].length).getValues();
var indexesToRemove = [];
for (var c = 25; c>2; c--){
if (sheet2.getRange(1,c).getValue() == 0)
{
indexesToRemove.push(c);
}
}
The part above works well. What is not working is the function to remove the columns from the array once I found the indexes to remove. The array _row is not what I am looking for. What am I doing wrong?
removeColumns(ary, indexesToRemove);}
function removeColumns(data, indexes) {
return data.map(function (row) {
// when we remove columns, the indexing gets off by 1 each time, keep track of how many to adjust
var indexAdjustment = 0;
// copy row w/ .slice so we do not modify the original array
var _row = row.slice();
indexes.forEach(function (colIndex) {
// remove column
_row.splice(colIndex - indexAdjustment, 1);
// add 1 to adjustment to account for the column we just removed
indexAdjustment++
});
return _row;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet2 = ss.getSheetByName('Cache');
sheet2.clear();
sheet2.getRange(2,1,_row.length,_row[0].length).setValues(_row);
});
}
BTW, I have also tried this before, but still not working:
var ary = sheet2.getRange(2,1,outData.length+1,outData[0].length).getValues();
for (var c = 25; c>2; c--){
if (sheet2.getRange(1,c).getValue() == 0)
{ ary = ary.map(function(item){
return item.splice(0,c)});
}
}
You want to delete the columns that the value of is 0 in the cells C1:Y1.
You want to reduce the process cost of the script.
You want to achieve this without using Sheets API.
Pattern 1:
In this pattern, at first, the cells which have the value of 0 from the cells C1:Y1 using TextFinder, and the columns are deleted from the retrieved cells using deleteColumn().
Sample script:
const sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("C1:Y1")
.createTextFinder(0)
.matchEntireCell(true)
.findAll()
.reverse()
.forEach(e => sheet.deleteColumn(e.getColumn()));
Pattern 2:
In this pattern, at first, all values are retrieved from "C1" to the last column for the all data rows, and delete the columns in the array and clear the range, and then, the values are put to the sheet. The method for directly processing the retrieved values has already been proposed. So as other pattern, I proposed the method which uses the transpose.
Sample script:
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getRange(1, 3, sheet.getLastRow(), sheet.getLastColumn() - 2);
const values = range.getValues();
const t = values[0].reduce((ar, r, i) => {
if (r != 0) ar.push(values.map(c => c[i]));
return ar;
}, []);
const v = t[0].map((_, i) => t.map(c => c[i]));
range.clearContent();
sheet.getRange(1, 3, v.length, v[0].length).setValues(v);
Pattern 3:
In this pattern, the request body for the batchUpdate method of Sheets API is created using the 1st row values, and the request body is used for requesting to Sheets API. By this, several columns can be deleted by one API call.
Before you run the script, please enable Sheets API at Advanced Google services.
Sample script:
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getActiveSheet();
const sheetId = sheet.getSheetId();
// Create rerequests for DeleteDimensionRequest.
const requests = sheet.getRange("C1:Y1")
.createTextFinder(0)
.matchEntireCell(true)
.findAll()
.reverse()
.map(e => {
const col = e.getColumn();
return {deleteDimension: {range: {sheetId: sheetId, dimension: "COLUMNS", startIndex: col - 1, endIndex: col}}}
});
// Request to the batchUpdate method using the request body.
Sheets.Spreadsheets.batchUpdate({requests: requests}, spreadsheet.getId());
In this case, requests is created using the method of pattern 1. Each request is as follows. You can see about this structure at the document.
{
"deleteDimension": {
"range": {
"sheetId": "###",
"dimension": "COLUMNS",
"startIndex": ##,
"endIndex": ##
}
}
}
References:
Class TextFinder
Advanced Google services
Method: spreadsheets.batchUpdate
DeleteDimensionRequest
function runOne() {
var d=0;
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var hA=sh.getRange(1,1,1,sh.getLastColumn()).getValues()[0];//header array
var vs=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).getValues();//data array
vs.forEach(function(r,j){
var d=0;
hA.forEach(function(h,i){
if(h==0)r.splice(i-d++,1);//removes elements in columns whose headers are == 0
});
});
Logger.log(vs);
}
Try using Sheets Advanced Service and batchUpdate.
Related
Remove all grouped rows / columns in a spreadsheet
Reference
https://developers.google.com/sheets/api/samples/rowcolumn#delete_rows_or_columns
Basically I have an array of information that I can currently append into google sheets, the thing is that a lot of the information is not necessary for my need so I wanted to find a way to just append the columns I need.
The picture above shows how everything looks,
basically the idea is to make it looks like in the following picture.
so basically I only need to append columns 4,5,7
currently why I do is this!
if (tozip.getContentType() == "application/zip"){ //for ZIP files
var unZip = Utilities.unzip(tozip); //assigns the unzipped file to a new variable
var table = Utilities.parseCsv(unZip[0].getDataAsString());// assigns the data to variable
for (var i = 0; i < table.length; i++) {//loops trought the array an appends the data as it goes.
sheet.appendRow(table[i]);
}
the data comes from a csv file and looks like this.
[[isApplication, applicationDate, isQualified, Funded_Date, isFunded, requested_loan_amount, amountFunded], [1, 2020-02-03, 1, 2020-02-03, 1, , 1300.0000], [1, 2019-12-29, 1, 2019-12-30, 1, 3000.0000, 2000.0000], [1, 2020-01-27, 1, 2020-01-28, 1, , 800.0000], [1, 2020-01-08, 1, 2020-01-10, 1, 2500.0000, 2500.0000], [1, 2020-02-04, 1, 2020-02-10, 1, , 1400.0000], [1, 2020-01-21, 1, 2020-01-21, 1, 5000.0000, 2000.0000], [1, 2020-02-06, 1, 2020-02-06, 1, 1100.0000, 1400.0000], [1, 2020-02-01, 1, 2020-02-04, 1, 1500.0000, 601.0000], [1, 2020-02-11, 1, 2020-02-11, 1, 500.0000, 800.0000]]
so yeah a lot of messy csv data.
I tried adding this to the code and a few variations of it so It can select the inside data
for (var i = 0; i < table.length; i++) {//loops trought the array an appends the data as it goes.
var columns = [];
columns.push(3);
columns.push(4);
columns.push(6);
sheet.appendRow(table[i][columns]);
}
but it does not work I'm super new to this type of stuff, so I'm pretty sure that's not the correct way to try and select the information I want from the array.
let me know if I need to elaborate more on this, I'm not super good at explaining this stuff.
Thank you in advance for the answers I really appreciate the help on this.
You want to retrieve the columns "D", "E" and "G" from the data retrieved by parsing the CSV data.
In your script, table of var table = Utilities.parseCsv(unZip[0].getDataAsString()); is the 2 dimensional data shown in your question.
You want to put the retrieved values to the Spreadsheet.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification points:
table of var table = Utilities.parseCsv(unZip[0].getDataAsString()); is 2 dimensional array.
When for (var i = 0; i < table.length; i++) {} is used, each row can be retrieved by table[i]. And the values from the columns "D", "E" and "G" can be retrieved by table[i][3], table[i][4], table[i][6].
In this modification, var values = [] is prepared, and each row is put with values.push([table[i][3], table[i][4], table[i][6]]).
When the method of appendRow() is used in the for loop, the process cost becomes high. So in this case, an array is created in the for loop. And the array is put to the Spreadsheet using setValues(). By this, the cost can be reduced.
When above points are reflected to your script, it becomes as follows.
Modified script:
var table = Utilities.parseCsv(unZip[0].getDataAsString());
// I modified below script.
var values = [];
for (var i = 0; i < table.length; i++) {
values.push([table[i][3], table[i][4], table[i][6]]);
}
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
Above script, the values are put to "Sheet1". If you want to change this, please modify getSheetByName("Sheet1").
In this case, table is var table = Utilities.parseCsv(unZip[0].getDataAsString()).
Note:
When var table = Utilities.parseCsv(unZip[0].getDataAsString()) doesn't return the array of CSV data, above modified script cannot be used. Please be careful this.
References:
parseCsv(csv)
getRange(row, column, numRows, numColumns)
setValues(values)
If I misunderstood your question and this was not the direction you want, I apologize.
I am writing a script for google sheet validation on localization tests. I've gotten stuck on some of the logic. The purpose of the script is to 1) Iterate through all tabs. 2) Find the column on row 2 that has the text "Pass/Fail". Lastly, 3) Iterate down that column and return the rows that say Fail.
The correct script to look at is called combined(). Step 1 is close to being correct, I think. Step 2 has been hard coded for the moment and is not dynamic searching the row for the text. Step 3 is done.
Any help would be great :)!!! Thanks in advance.
https://docs.google.com/spreadsheets/d/1mJfDtAi0hHqhqNB2367OPyNFgSPa_tW9l1akByaTSEk/edit?usp=sharing
/*This function is to cycle through all spreadsheets.
On each spreadsheet, it will search the second row for the column that says "Pass/Fail".
Lastly, it will take that column and look for all the fails and return that row*/
function combined() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var r =[];
for (var i=0 ; i<sheets.length ; i++){//iterate through all the sheets
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues(); // read all data in the sheet
//r.push("test1"); //Testing to make sure all sheets get cycled through
/*I need something here to find which column on row two says "Pass/Fail"*/
for(i=3;i<data.length;++i){ // iterate row by row and examine data in column A
//r.push("test2"); //Testing to make sure the all
if(data[i][7]=='Fail'){ r.push(data[i])}; // if column 7 contains 'fail' then add it to the list
}
}
return r; //Return row of failed results on all tabs
}
At first, it retrieves data at column g. It retrieves a result from the data. The result is 2 dimensional array. The index of each element of the 2D array means the sheet index. If the sheet doesn't include values in column g, the element length is 0.
For example, in the case of following situation,
Sheet 0 doesn't include values in column g.
Sheet 1 includes values in column g. There are "Fail" value at the row number of 3, 4, 5.
Sheet 2 includes values in column g. There are "Fail" value at the row number of 6, 7, 8.
The result (return r) becomes below.
[[], [3, 4, 5], [6, 7, 8]]
Sample script 1:
function combined() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var data =[];
sheets.forEach(function(ss){
try { // In the case of check all sheets, if new sheet is included in the spreadsheet, an error occurs. This ``try...catch`` is used to avoid the error.
data.push(ss.getRange(3, 7, ss.getLastRow(), 1).getValues());
} catch(e) {
data.push([]);
}
});
var r = [];
data.forEach(function(e1, i1){
var temp = [];
e1.forEach(function(e2, i2){
if (e2[0] == "Fail") temp.push(i2 + 3);
});
r.push(temp);
});
return r;
}
If I misunderstand your question, I'm sorry.