Find inside 2 dimensional Array Using Google Apps Script - javascript

I have values in an array which are:
[8:00 AM, 9:00 AM] //result of another function
And I would like to find their values inside this 2-Dimensional Array:
[
[8:00 AM],
[9:00 AM],
[10:00 AM],
[11:00 AM],
[12:00 NN],
[1:00 PM],
[2:00 PM],
[3:00 PM]
]
and change the their values "Not Available"
The array log are the values from google sheet using this code:
function testRow(){
var lookDate = "Aug 28, 2019";
var ss = SpreadsheetApp.openByUrl(url);
var ts = ss.getSheetByName("Time_Select");
var checkData = ts.getRange(1, 1, 1, ts.getLastColumn()).getDisplayValues()[0];
var index = checkData.indexOf(lookDate)+1;
var timeValues = ts.getRange(2, index, ts.getLastRow()-1, 1).getValues();
Logger.log(timeValues)
/*var checkSplit = dateValues.join().split(",");
var checkMe = checkSplit.indexOf(dataDisable[1]);
var timeValues = ts.getRange(checkMe, index).getValue();*/
}
I tried to use this code (as you can see in the above google script code):
var checkSplit = dateValues.join().split(",");
var checkMe = checkSplit.indexOf(dataDisable[1]);
var timeValues = ts.getRange(checkMe, index).getValue();
but it was giving me a wrong value. Do you have any suggestions or solutions on how can I search the values inside the 2 dimensional array then go to its location in google sheet and change its value to "Not Available"? Thank you very much in advance for the help.
here is the link for my google sheet:
https://docs.google.com/spreadsheets/d/1lEfzjG1zzJVPMN8r-OpeZm6q9_IqSwk9DNCEY-q7ozY/edit?usp=sharing

The output from your timeValues are strings. If the contents of [8:00 AM, 9:00 AM] are also strings, this is how you can compare them and set the rescpective cell values to "Not Available" in case of coincidence:
function testRow(){
var lookDate = "Aug 28, 2019";
var ss = SpreadsheetApp.openByUrl(url);
var ts = ss.getSheetByName("Time_Select");
var checkData = ts.getRange(1, 1, 1, ts.getLastColumn()).getDisplayValues()[0];
var index = checkData.indexOf(lookDate)+1;
// I called your array resulting from another function 'times'
var times=['8:00 AM', '9:00 AM'];
var timeValues = ts.getRange(2, index, ts.getLastRow()-1, 1).getValues();
for(var i=0;i<times.length;i++){
for(var j=0;j<timeValues.length;j++){
if(times[i]==timeValues[j][0]){
timeValues[j][0]="Not Available"
}
}
}
ts.getRange(2, index, ts.getLastRow()-1, 1).setValues(timeValues);
}
Basically, you loop through both arrays and when the contents coincide you replace the respective entry of timeValues with "Not Available". After exiting the loops you assign to your range the values of the updated timeValues array.

How about:
var result = timeValues.map((_tv) => { return [_tv]; });

Related

How do I iterate through my google sheets rows, update an array, and show results in another sheet?

I am trying to write a Google Apps Script function which scans every row of my spreadsheet, and if column 36 has a specific string, will look at column 31, column 23, etc. of that row to create an array with the data in these columns.
I think it will ultimately require an array updating function inside an if statement inside a loop.
Loop through every row of column 36
If string matches target string
Add row's data to an array
Update another sheet with data from this array
Thank you!
Here is what I've tried so far:
`
function myFunction() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Database");
var range = sheet.getRange(2, 36).getValue();
Logger.log(range);
}
`
Here is the code, replace the string of Target Sheet Name, the specific String and etc on your own.
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// DataBase sheet:
const dbSheet = ss.getSheetByName("Database");
// Target sheet for placing your outputs:
const tgSheet = ss.getSheetByName("Target Sheet Name");
// Get all values as a 2D array from DataBase sheet:
const sourceValues = dbSheet.getDataRange().getValues();
// Declare result array.
const results = [];
// Loop through every row:
for (const row of sourceValues) {
const specificString = 'Your String';
// check the value of column 36 of each row.
const check = row[36] === specificString;
// if condition check returns true, form an array with column 31, column 23 and 'etc' of this row, than push it into the result array.
if(check) results.push([row[31],row[23],'etc']);
}
// print the result onto your Target Sheet, start from A1.
tgSheet.getRange(1,1,results.length,results[0].length).setValues(results);
}
Move selected rows to another sheet
function elfunko(specificvalue) {
const ss = SpreadsheetApp.getActive();
const vs = ss.getSheetByName("Database").getDataRange().getValues().filter(r => r[35] == specificvalue);
const sh = ss.getSheetByName("Sheet1");
sh.getRange(sh.getLastRow() + 1, 1, vs.length, vs[0].length).setValues(vs);
}

Why is my reduceRegion() function extracting mean raster values for only some features?

I am trying to Derive mean estimates of 2019 Sentinel columnar aerosols for all Philadelphia census tracts.
My script below produces mean estimates for tracts, but only about 50% of them. The 2019 image (aerosol index band) is complete and does not contain any null or missing values. I think something is going wrong with my reduceRegion() function. Can anyone tell what is going wrong?
// 4.1 - filter an ImageCollection for one band one year of images
var getOneYearImg = function(year,inImageCollection,selectBand,inReducer) {
var startDateString = year + '-01-01';
var startDate = ee.Date.parse('YYYY-MM-dd',startDateString);
var endDate = startDate.advance(1,'year');
var annualImg = inImageCollection
.select(selectBand)
.filterDate(startDate,endDate)
.reduce(inReducer);
return(annualImg);
};
// 4.2 - calculate annual average of the aerosol index
var getPollutantAnnualAvg = function(year) {
var reducerType = ee.Reducer.mean();
var AOI_avg = getOneYearImg(2019,ee.ImageCollection('COPERNICUS/S5P/OFFL/L3_AER_AI'),'absorbing_aerosol_index',reducerType);
var PollutantImage = AOI_avg
return(PollutantImage);
};
// 4.3 - Extract 2019 averages for each tract in the PhilaTracts FeatureCollection
// SOMETHING IS GOING WRONG AT THIS STEP. Perhaps it's the step where we extract estimates for the input feature object?
var extractPollutants = function(inFeature) {
var yearOfInterest = 2019;
var PollutantImage = getPollutantAnnualAvg(yearOfInterest);
var extractedPollutants = PollutantImage.reduceRegion(
ee.Reducer.mean(),
inFeature.geometry(),
10000
);
inFeature = inFeature.set({
'Aerosol_Avg':extractedPollutants.get('absorbing_aerosol_index_mean')
});
return inFeature;
};
var PollutantEstimates = PhilaTracts.map(extractPollutants);
print(PollutantEstimates.first());
Map.addLayer(PollutantEstimates);
//Step 4: Load the census tract and use a map function to append mean AOI to the attribute table
var PhilaTract_AOI = PhilaTracts.map(extractPollutants);
print(PhilaTract_AOI);
Export.table.toDrive({
collection: PollutantEstimates,
description: 'PollutantEstimates',
fileFormat: 'CSV'
});

Google Sheets, stack report from multiple workbooks

Goal: To stack data from 90+ google workbooks, all with the same sheet name, into the one master sheet for reporting
Info:
All worksheets have the same number of columns.
I have the following script but it does not run properly, I think the issue is with how I am caching / Pushing the data to the array before pasting to the output sheet.
I am trying to build an array then paste it in one go.
The tables I am stacking have 47 columns, unknown number of rows.
The part that opens the sheets is all working perfectly.
// Get the data from the worksheets
var indexsheet = SpreadsheetApp.getActive().getSheetByName("Index");
var outputsheet = SpreadsheetApp.getActive().getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL)
var CurrentCycleName = response.getResponseText()
// Assign datasets to variables
var indexdata = indexsheet.getDataRange().getValues();
// For each workbook in the index sheet, open it and copy the data to a cache
indexdata.forEach(function(row, r) {
try {
//open Entity specific workbook
var workbookid = indexsheet.getRange(r + 1, 7, 1, 1).getValues();
var Entityworkbook = SpreadsheetApp.openById(workbookid)
// Open workhseet
Entitysheet.getSheetByName(CurrentCycleName)
// Add PR Data to cache - stacking for all countrys
var PRDataCache = Entitysheet.getDataRange().push()
} catch {}
})
// Set the all values of the sheet at once
outputsheet.getRange(r + 1, 14).setValue('Issue Splitting Data')
Entitysheet.getRange(2, 1, PRDataCache.length || 1, 47).setValues(PRDataCache)
};
This is the index tab where we are getting the workbookid from to open each file
This is the output file, we are stacking all data from each country
I believe your goal is as follows.
You want to retrieve the Spreadsheet IDs from the column "G" of "Index" sheet.
You want to give the specific sheet name using a dialog.
You want to retrieve all values from the specification sheet in all Spreadsheets. In this case, you want to remove the header row.
You want to put the retrieved values on "Output" sheet.
In this case, how about the following sample script?
Sample script:
function myFunction() {
var ss = SpreadsheetApp.getActive();
var indexsheet = ss.getSheetByName("Index");
var outputsheet = ss.getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
var CurrentCycleName = response.getResponseText();
var ids = indexsheet.getRange("G1:G" + indexsheet.getLastRow()).getValues();
var values = ids.reduce((ar, [id]) => {
try {
var [, ...values] = SpreadsheetApp.openById(id).getSheetByName(CurrentCycleName).getDataRange().getValues();
ar = [...ar, ...values];
} catch (e) {
console.log(`"${id}" was not found.`);
}
return ar;
}, []);
if (values.length == 0) return;
// If the number of columns is different in all Spreadsheets, please use the following script.
// var maxLen = Math.max(...values.map(r => r.length));
// values = values.map(r => r.length < maxLen ? [...r, ...Array(maxLen - r.length).fill("")] : r);
outputsheet.getRange(outputsheet.getLastRow() + 1, 1, values.length, values[1].length).setValues(values);
}
Note:
When the number of Spreadsheet IDs is large, the processing time might be over 6 minutes. I'm worried about this. At that time, how about separating the Spreadsheet IDs?
Reference:
reduce()

Retrieving a full row of google sheets data to JavaScript

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.

Javascript find all occurrences of name in array of objects and create new unique arrays

I'm trying to get all occurrences from a google calendar in Google Apps Script and create individual arrays for each event name.
Simplified version (example) of response array:
{summary=Name1, start={dateTime=2018-12-03T15:00:00+01:00}, end={dateTime=2018-12-03T23:00:00+01:00}},
{summary=Name2, start={dateTime=2018-12-04T11:00:00+01:00}, end={dateTime=2018-12-04T23:00:00+01:00}},
{summary=Name1, start={dateTime=2018-12-05T07:00:00+01:00}, end={dateTime=2018-12-05T15:00:00+01:00}}
What I can't figure out is how to filter/split (whatever you'd call it) this up so I'd end up with a new array with the following format:
EDIT
{Name1=[[2018-12-03, 15, 23, 8.0], [2018-12-04, 11, 23, 12.0], [2018-12-05, 7, 15, 8.0], [2018-12-06, 15, 23, 8.0]], Name2=[[2018-12-11, 7, 16, 9.0], [2018-12-12, 7, 16, 9.0]]}
The idea is to then iterate through this new array and do a foreach to get a list of all the dates for individual names.
This is as far as I've gotten
function hoursTally() {
var calendarId = [CALENDAR_ID];
var startDay = 24;
var endDay = 23;
var month = parseFloat(new Date().getMonth()).toFixed(0);
var year = new Date().getYear();
var startDate = new Date( year, month-1, startDay );
var endDate = new Date( year, month, endDay );
var optionalArgs = {
timeMin: startDate.toISOString(),
timeMax: endDate.toISOString(),
showDeleted: false,
singleEvents: true,
orderBy: 'startTime'
};
var response = Calendar.Events.list(calendarId, optionalArgs);
var events = response.items;
events.forEach(function(e){
Logger.log(e);
var name = e.summary;
var eventDateStart = new Date(e.start.dateTime);
var eventDateEnd = new Date(e.end.dateTime);
var startTime = parseFloat(eventDateStart.getHours()).toFixed(0);
var endTime = parseFloat(eventDateEnd.getHours()).toFixed(0);
var theDate = Utilities.formatDate(eventDateStart, 'GMT+1', 'yyyy-MM-dd');
var total = endTime-startTime;
});
}
Every attempt of looping the events and getting the aforementioned format has failed :(
Since your stated goal is to collect information from each similarly named event into a single summary object, your output data structure should not be an Array of objects - it should just be an associative object. An Array would be appropriate if you wanted equivalent objects to remain distinct, but you state this is not the case.
The solution is then to reduce the returned events into an object, where the key of the data is the name, and the value is an array of instance information. The instance information is itself an array (in your example, [2018-12-03, 15, 23, 8])
A simple example which you can adapt to your use case:
const summary = items.reduce(function (obj, item) {
var name = item.summary;
// If we haven't seen this name before, initialize an empty array
if (obj[name] === undefined) { obj[name] = []; }
...
// Create an array with the info we want to store
var info = [
eventStartDate,
...
];
// Store this info array with all the others for the same name
obj[name].push(info);
return obj;
}, {});
You then use this summary by iterating the object:
for (var name in summary) {
summary[name].forEach(function (info) {
...
});
...
}
Array#reduce

Categories

Resources