Faster way to collect data from JSON than looping in spreadsheet - javascript

I am learning Javascript and this is my first time working with Google Sheets Apps Script. What I am doing is taking a large JSON file and importing it into my sheet. After that I am populating a few hundred properties based on the key:value found in the JSON.
This is how it kinda works right now:
Go to first column and first row of my sheet.
Get the name (property name).
Search the JSON for the key and then grab the value.
Update a neighbor cell with the value found in the JSON.
Right now it all works the only issue is it seems to be pretty slow. It takes about .5-1 second per lookup and when I have 200+ properties it just seems slow. This might just be a limitation or it might be my logic.
My sheet can be found here: https://docs.google.com/spreadsheets/d/1tt3eh1RjL_CbUIaPzj10DbocgyDC0iNRIba2B4YTGgg/edit#gid=0
My function that does everything:
function parse() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange(2,1);
var range1 = sheet.getRange("A2");
var cell = range.getCell(1, 1);
var event_line = cell.getValue();
var tmp = event_line.split(". ");
var col_number = tmp[0];
var event_name = tmp[1];
event_json = get_json_from_cell(col_number);
const obj = JSON.parse(event_json);
var traits = obj.context.traits;
var properties = obj.properties;
//Get the range for the section where properties are
var traits_range = sheet.getRange("contextTraits");
var allprop = sheet.getRange("testAll");
var alllen = allprop.getNumRows();
var length = traits_range.getNumRows();
for (var i = 1; i < length; i++) {
var cell = traits_range.getCell(i, 1);
var req = traits_range.getCell(i, 4).getValue();
var trait = cell.getValue();
var result = traits[trait];
var result_cell = traits_range.getCell(i, 3);
if (result == undefined) {
if (req == "y"){
result = "MISSING REQ";
result_cell.setBackground("red");
} else {
result = "MISSING";
result_cell.setBackground("green");
}
} else {
result_cell.setBackground("blue");
}
result_cell.setValue(result);
Logger.log(result);
}
for (var i = 1; i < alllen; i++) {
var cell = allprop.getCell(i,1);
var req = allprop.getCell(i, 4).getValue();
var prop = cell.getValue();
var result = properties[prop];
var result_cell = allprop.getCell(i, 3);
if (result == undefined) {
if (req == "y"){
result = "MISSING REQ";
result_cell.setBackground("red");
} else {
result = "MISSING";
result_cell.setBackground("green");
}
} else {
result_cell.setBackground("blue");
}
result_cell.setValue(result);
}
Logger.log(result);
}

Related

Showing errors, the data validation rule has more items than the limit of 500. Use the list from a range criteria instead

The following codes showing errors due to limit of 500. I tried to solve it and reviewed many docs but failed. Is there any way to solve it?
var spreadsheet = SpreadsheetApp.getActive();
var dashboard = spreadsheet.getSheetByName("Dashboard");
var wsOptions = spreadsheet.getSheetByName("Master");
var options = wsOptions.getRange(2, 1, wsOptions.getLastRow()-1,6).getDisplayValues();
function onEdit(e){
var as = e.source.getActiveSheet();
var val = e.range.getValue();
var val_not = e.range.getA1Notation();
if (val_not =='F5' && as.getName() == "Dashboard"){
if(val === "All"){
dashboard.getRange('G5').setValue(new Date()).setNumberFormat("yyyy-mm-dd");
dashboard.getRange('G5').clearDataValidations();
}
else{
var filteredOptions = options.filter(function(o){return o[5] === val});
var listToApply = filteredOptions.map(function(o){return o[0]}).sort().reverse();
var cell = dashboard.getRange('G5');
var rule = SpreadsheetApp.newDataValidation().requireValueInList(listToApply).setAllowInvalid(false).build();
cell.clearContent();
cell.clearDataValidations();
cell.setDataValidation(rule);
}
}
}
Explanation:
As the error suggests:
The data validation rule has more items than the limit of 500. Use the
‘List from a range’ criteria instead.
you should use requireValueInRange().
In order to use the latter, you need to define a range of data validation items. In the following approach, I create a range of these items opt_range and I use that as an argument for the requireValueInRange() function.
Solution 1:
function onEdit(e){
var as = e.source.getActiveSheet();
var val = e.range.getValue();
var val_not = e.range.getA1Notation();
var spreadsheet = SpreadsheetApp.getActive();
var dashboard = spreadsheet.getSheetByName("Dashboard");
var wsOptions = spreadsheet.getSheetByName("Master");
var options = wsOptions.getRange(2, 1, wsOptions.getLastRow()-1,6).getDisplayValues();
if (val_not =='F5' && as.getName() == "Dashboard"){
if(val === "All"){
dashboard.getRange('G5').setValue(new Date()).setNumberFormat("yyyy-mm-dd");
dashboard.getRange('G5').clearDataValidations();
}
else{
var filteredOptions = options.filter(function(o){return o[5] === val});
var listToApply = filteredOptions.map(function(o){return o[0]}).sort().reverse();
var listToApply2D = listToApply.map(ta=>[ta]);
var jSize = wsOptions.getRange('J:J').getValues().filter(String).length;
if (jSize>0){ wsOptions.getRange(1,10,jSize,1).clearContent()};
wsOptions.getRange(1,10,listToApply2D.length,listToApply2D[0].length).setValues(listToApply2D);
var opt_range = wsOptions.getRange(1,10,listToApply2D.length,listToApply2D[0].length);
var cell = dashboard.getRange('G5');
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(opt_range).build();
cell.clearContent();
cell.clearDataValidations();
cell.setDataValidation(rule);
}
}
}
Solution 2 (recommended):
Assuming you don't have 500 unique items in the list, you can still use the code you posted, but take the unique list of the items:
function onEdit(e){
var spreadsheet = SpreadsheetApp.getActive();
var dashboard = spreadsheet.getSheetByName("Dashboard");
var wsOptions = spreadsheet.getSheetByName("Master");
var options = wsOptions.getRange(2, 1, wsOptions.getLastRow()-1,6).getDisplayValues();
var as = e.source.getActiveSheet();
var val = e.range.getValue();
var val_not = e.range.getA1Notation();
if (val_not =='F5' && as.getName() == "Dashboard"){
if(val === "All"){
dashboard.getRange('G5').setValue(new Date()).setNumberFormat("yyyy-mm-dd");
dashboard.getRange('G5').clearDataValidations();
}
else{
var filteredOptions = options.filter(function(o){return o[5] === val});
var listToApply = filteredOptions.map(function(o){return o[0]}).sort().reverse();
var uniqueList = listToApply.filter((v, i, a) => a.indexOf(v) === i); // <= New code
var cell = dashboard.getRange('G5');
var rule = SpreadsheetApp.newDataValidation().requireValueInList(uniqueList).setAllowInvalid(false).build();
cell.clearContent();
cell.clearDataValidations();
cell.setDataValidation(rule);
}
}
}
Instead of using requireValueInList use requireValueInRange. This implies that you should add the values in the list to a range.
Resources
https://developers.google.com/apps-script/reference/spreadsheet/data-validation-builder#requireValueInRange(Range)
https://developers.google.com/apps-script/reference/spreadsheet/data-validation-builder#requirevalueinrangerange,-showdropdown

Delete Duplicate Data using Google Script

I am trying to make a script to automate deleting duplicate data based on column A. This is the current script I am using and it works.
// This scripts works but deleting new data instead of old data
function removeDuplicates() {
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName(sheetName);
var vA = sh.getDataRange().getValues();
var hA = vA[0];
var hObj = {};
hA.forEach(function(e, i) {
hObj[e] = i;
});
var uA = [];
var d = 0;
for (var i = 0; i <= vA.length; i++) {
if (uA.indexOf(vA[i][hObj['key']]) == -1) {
uA.push(vA[i][hObj['key']]);
} else {
//sh.deleteRow(i + 1 - d++);
sh.deleteRow((parseInt(i)+1) - d);
d++;
}
}
};
But this one is deleting the newly added row, what I want to achieve is it should delete the old duplicate row instead. How can I do that?
In else part instead of using i which represent your current row, use the indexOf the row you want to delete. Delete it first and then push the new one into array
// This scripts works but deleting new data instead of old data
function removeDuplicates() {
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName(sheetName);
var vA = sh.getDataRange().getValues();
var hA = vA[0];
var hObj = {};
hA.forEach(function(e, i) {
hObj[e] = i;
});
var uA = [];
var d = 0;
for (var i = 0; i <= vA.length; i++) {
if (uA.indexOf(vA[i][hObj['key']]) == -1) {
uA.push(vA[i][hObj['key']]);
} else {
//sh.deleteRow(i + 1 - d++);
let j = uA.indexOf(vA[i][hObj['key']]);
sh.deleteRow((parseInt(j)+1) - d);
d++;
uA.push(vA[i][hObj['key']]);
}
}
};

Update orders status Google Spreadsheet by checking the status in other Sheet using Google Apps Scripts

I'm working to get the status for orders to be updated in the master sheet. I have disconnected orders with status in another sheet named "Decommission sheet". While I update the master I need to check the status for these orders by applying the below logic:
If the order is DECOMMISSIONED && not available in decommission sheet
then the order is LIVE.
If the order is available in the decommission sheet then check the
status if it is DECOMMISSIONED then the order is DECOMMISSIONED.
I have done it in a totally different way but it doesn't work with me. Any help would be appreciated.
function pathstatus() {
var MasterSs = SpreadsheetApp.openById('ID');
var MsterSh = MasterSs.getSheetByName('Master Sheet');
var MasterData = MsterSh.getDataRange().getValues();
var DecommisstionSh = MasterSs.getSheetByName('Decommisstion');
var DecommisstionData=DecommisstionSh.getDataRange().getValues();
for(var x=0;x<MasterData.length;x++){
var MasterPathName =MasterData[x][2]
var Masterstatus=MasterData[x][6]
var MasterStage=MasterData[x][7]
if(MasterStage == "DECOMMISSIONED"){
for(var i=0;i<DecommisstionData.length;i++){
var DecommisstionPathName = DecommisstionData[i][2]
var DecommisstionStatus = DecommisstionData[i][7]
var DecommissionedDate = DecommisstionData[i][10]
if(DecommisstionPathName == MasterPathName && DecommisstionStatus == "COMPLETED") {
MasterData[x][6]="DECOMMISSIONED"
MasterData[x][12]=DecommissionedDate
}else {
MasterData[x][6]="LIVE"
}
}
}
}
MsterSh.getRange(2,1,MsterSh.getLastRow(),MsterSh.getLastColumn()).clearContent();
MsterSh.getRange(2,1,MasterData.length,MasterData[0].length).setValues(MasterData)
SpreadsheetApp.flush()
}
In another way
function myFunction() {
var MasterSs = SpreadsheetApp.openById('ID');
var MsterSh = MasterSs.getSheetByName('Master Sheet');
var MasterData = MsterSh.getDataRange().getValues();
var DecommisstionSh = MasterSs.getSheetByName('Decommisstion');
var DecommisstionData=DecommisstionSh.getDataRange().getValues();
MasterData.splice(0,1);
DecommisstionData.splice(0,1);
var Decommisstionpath = [];
var Decommisstionstatus = [];
for(var i=0;i<DecommisstionData.length;i++) {
Decommisstionpath.push(Number(DecommisstionData[i][2]))
Decommisstionstatus.push(DecommisstionData[i][7])
}
var i=0;
for(var x=0;x<MasterData.length && MasterData[x][3] != undefined ;x++) {
var OrderStage = MasterData[x][8]
if(OrderStage=='DECOMMISSIONED') {
var PathName = MasterData[x][2]
var index = Decommisstionpath.indexOf(PathName);
if(index == -1)
{MasterData[x][6]="LIVE" }
else{
MasterData[x][6]="Check"
}
}
}
MsterSh.getRange(2,1,MsterSh.getLastRow(),MsterSh.getLastColumn()).clearContent();
MsterSh.getRange(2,1,MasterData.length,MasterData[0].length).setValues(MasterData)
SpreadsheetApp.flush();
}
Neither function works properly

javascript google script appendRow fails Service Error

I have a Google Apps Script that has been running for 3 months and starting a few weeks ago I'm getting a "Service Error" on the Appendrow function which I've bolded below. Any ideas on how to fix this?
function updateACOPS2(){
var ss = SpreadsheetApp.openById(".....")
var sheetSubmission = ss.getSheetByName("Sheet8");
var dataSubmission = sheetSubmission.getDataRange().getValues();
var lastColSubmission = sheetSubmission.getLastColumn();
var ssActive = SpreadsheetApp.openById("....")
var sheetActive = ssActive.getSheetByName("AcopsAll");
var sheetMain = ssActive.getSheetByName("Sheet1");
var dataActive = sheetActive.getDataRange().getValues();
var lastrow = sheetActive.getLastRow();
for(var i = 1; i < dataSubmission.length && dataSubmission[i][2] != ""; i++){
var currentIDSubmission = dataSubmission[i][2] + dataSubmission[i][3];
var checkGotMatch = false;
var toCopyRow = sheetSubmission.getRange(i+1,1,1,71);
// copy entire row for new record
Logger.log(currentIDSubmission);
// if there is a matching record flag as matched
for(var j = 1; j<dataActive.length; j++){
var currentIDActive = dataActive[j][2] + dataActive[j][3];
var currentIDSub = dataSubmission[i][2];
if(currentIDSub != '' && currentIDSubmission == currentIDActive){
checkGotMatch = true;
Logger.log(currentIDActive);
break;
}
}
// if it is a new record Append entire row
if(currentIDSub != '' && checkGotMatch == false){
**sheetMain.appendRow(toCopyRow.getValues()[0]);**
}
}
SpreadsheetApp.flush();
ss.toast("ACOPS Active has been updated.", "Complete");
}
In appendRow you need to pass an array so update your appendRow and try
sheetMain.appendRow([toCopyRow.getValues()[0]])

How to empty an Array in a Script

I have a script that uses AJAX/PHP/SQL to query data and pushes it into an array with a bunch of IF's statements. The changeData function is called every 6 seconds. The first query I return I have 6 arrays. The second time i send a request, my push array(IsVacant1) is double and went to 12. after a while, I have over 500 arrays going into my .each statement.
How do I 'clear' this every time I make a request so that I am not adding arrays? Any help is most appreciated.
function changeData() {
isPaused = true;
var mydata0 = null;
$.post('php/ProductionChange.php', {
'WC': cc
}, function(data) { // This is Where I use an AJAX call into a php file.
mydata0 = data; // This takes the array from the call and puts it into a variable
var pa = JSON.parse(mydata0); // This parses the data into arrays and elements
var temp = {};
var bayData = '';
if (pa != null) {
for (var i = 0; i <= pa.length - 1; i++) {
var job = pa[i][0];
var shipdate = pa[i][1];
var status = pa[i][2];
var name = pa[i][3];
var EnclLoc = pa[i][13];
var Enclsize = pa[i][14];
var backpan = pa[i][15];
var percentCom = pa[i][16];
var IsVisible = pa[i][17];
var png = pa[i][18];
var WorkC = pa[i][20];
baydata = 'bayData' + i + '';
temp = {
job, shipdate, name, EnclLoc, Enclsize, backpan, percentCom, IsVisible, png, WorkC, status
};
isVacant1.push({
baydata: temp
});
}
} else {
ii = 1;
//alert("There are no more job numbers in this bay location. Thank you. ");
}
$.each(isVacant1, function(key, value) {
var job = value.baydata.job;
var ship = value.baydata.shipdate;
var name = value.baydata.name;
var encl = value.baydata.EnclLoc;
var EnclSize = value.baydata.EnclLoc;
var percentCom = value.baydata.percentCom;
var backpan = value.baydata.backpan;
var PngLogo = value.baydata.png;
var IsVisible = value.baydata.IsVisible;
var WorkC = value.baydata.WorkC;
var status = value.baydata.status;
var p = WorkC;
WorkC = (WorkC < 10) ? ("0" + WorkC) : WorkC;
//// remember if the encl location matches the workcell cell then do stuff based on that....... hint encl image not hiding becase of duplicate 17s
if (((encl == p) || (backpan == p)) && job != 123) {
$('#WC' + p).show();
document.getElementById("bayData" + p).innerHTML = name + ' ' + ship; // Work Cell Name and Ship Date
document.getElementById("bayData" + p + "a").innerHTML = job; // Work cell Job Number
document.getElementById("percentCom" + p).innerHTML = percentCom + '%'; // Work Cell Percent Complete
} else {
$('#WC' + p).hide();
From your question it looks like you want to clear the isVacant1 array.
In your ajax callback just put isVacant1 = []; as the first line. Like this
function(data) { // This is Where I use an AJAX call into a php file.
isVacant1 = [];
mydata0 = data; // This takes the array from the call and puts it into a variable
var pa = JSON.parse(mydata0); // This parses the data into arrays and elements
var temp = {};
var bayData = '';
..................
From your code it's not clear how you are declaring/initializing isVacant1 so i have suggested isVacant1 = [] otherwise you can also use isVacant1.length = 0.
You can also take a look here How do I empty an array in JavaScript?

Categories

Resources