Sheet order not respecting index in google sheets in chrome - google-apps-script

This spreadsheet has a script that updates the tabs and labels them in response to the week ending date entered. It also rearranges them based on the selected last day of the week (Saturday or Sunday for example). The spreadsheet formulas calculate the order of the days and the day and day of the month labelling. These are read into the script from named ranges.
It works fine on my IOS devices and sometimes works properly in chrome desktop. But (in chrome desktop) it gets into a mode where one sheet is always out of position. In the example, I label the tabs with the sequence position (0 to 6) and the returned sheet index, so it's clear that the problem is alignment between the object model and the rendering. As you can see in the following pic, the Monday sheet is out of position, if I change the end of week day again, it will be the one out of position again.
How can I force google sheets to respect the sheet index?
I tried flush but no joy. The only way I can get it to line up reliably is to close and re-open the sheet.
function onEdit(e) {
var source = e.range;
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(!qualifiedSource(e.range, ["selectedDate", "lastDayOfWeek"].map(function(n) {
return ss.getRangeByName(n);
}))) return;
var fmtedNames = WeekEndingDate2();
// add indexing to the sheet names
ss
.getSheets()
.filter(function(sht) { return fmtedNames.indexOf(sht.getName()) != -1 })
.forEach(function (s, i) { s.setName(s.getName() + ":" + i + ":" + s.getIndex()); });
}
function qualifiedSource (source /*range*/, target /*range | range[]*/) {
if(!isArray(target)) target = [target];
return target.some(function(t) {
return source.getSheet().getName() == t.getSheet().getName() && source.getA1Notation() == t.getA1Notation();
});
}
function WeekEndingDate2() {
var _wb = SpreadsheetApp.getActiveSpreadsheet();
var _days = _wb.getRangeByName("daysOfWeek");
var _daysFmt = _wb.getRangeByName("daysOfWeekFmt");
return (function _update() {
var daySheets = SheetsCollection();
var days = _days.getValues()[0];
var daysFmt = _daysFmt.getValues()[0];
daySheets
.Add(days.map(namesToSheets))
.Sort(daysFmt);
return daysFmt;
function namesToSheets(d, i) {
var allSheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var targetSheet;
allSheets.some(function(sht) {
return targetSheet = sht.getName().indexOf(d) === 0 ? sht : null;
});
Logger.log(Utilities.formatString("%s\t%s", d, targetSheet.getName()));
if (targetSheet == null)
throw new Error("Error: Missing sheet for " + d);
return targetSheet.setName(daysFmt[i]);
}
})();
}
function SheetsCollection () {
var _sheets = {}; // hash of WrappedSheets
var _maxIndex = 0;
function _hash (n) {
return n.replace(" ", "_");
}
function _addItem (s /*worksheet*/) {
_sheets[_hash(s.getName())] = WrappedSheet(s);
_maxIndex = Math.max(_maxIndex, s.getIndex())
}
function _add (s /*worksheet | worksheet[]*/) {
if(Array.isArray(s))
s.forEach(_addItem);
else
_addItem(s);
return this;
}
function _sort ( sortOrder /*range | string[]*/, delay /* int milliseconds */) {
var sortedNames = sortOrder.getValues ? sortOrder.getValues()[0] : sortOrder;
var namesLength = sortedNames.length;
var i, sht;
for each (var name in sortedNames) {
Logger.log(name);
_sheets[_hash(name)].MoveTo(_maxIndex);
if(delay) Utilities.sleep(delay);
}
return this;
}
return {
Add: _add,
Sort: _sort
}
}
function WrappedSheet(sheet /*string || sheet*/) {
var _wb = SpreadsheetApp.getActive();
var _sheets = _wb.getSheets();
var _shtName = typeof sheet == "string" ? sheet : sheet.getName();
function _moveTo (to /*integer*/) {
var insertAt = to;
var actSht = _wb.getActiveSheet();
var maxAttempts = 10;
var attempt = 1;
var before = this.Sheet.getIndex();
Logger.log(listSheets(""));
_wb.setActiveSheet(this.Sheet);
_wb.moveActiveSheet(insertAt);
_wb.setActiveSheet(actSht);
Logger.log("%s -> %s after %s", this.Name, this.Sheet.getIndex(), attempt -1);
Logger.log(listSheets(""));
}
return {
MoveTo: _moveTo,
get Sheet() { return _wb.getSheetByName(_shtName); },
get Position() { return _wb.getSheetByName(_shtName).getIndex(); },
Name: _shtName
}
}

I faced the exact same problem as you with a very similar functioning script. Unfortunately, there is no 'fix' for this issue, it seems to be a caching issue with the browser. The only way around it I found was to refresh the window twice or close and reopen the spreadsheet and then the tabs appear in the correct order.

Related

How to display info from other sheets, specific to text in cells

Our company works at different properties repairing appliances, I would like to build a database to search up the information on each appliance at specific properties and in their specific apt/units, I created a form to start this process, but I need help with some complex coding.
I first created a box for the property, then I created an "Apt/Unit" box. The idea is when I select a property, the units tied to that property are shown in dropdown/type searchable list in the Apt/Unit box.
I then created an "Appliance type" box. The idea is when the "Apt/Unit" is selected, it will display the dropdown/type searchable list of the appliances tied to that specific "Apt/Unit".
Then I created boxes for the info for the appliance (Brand, Model #, Serial #, & Color), this is a bit more self-explanatory - once the appliance type is selected, it will display the respective information for each box for that appliance.
Here's the link to the Google sheet: https://docs.google.com/spreadsheets/d/1JZhEYjk5xVN3uOc_Ucb8HFr6d96XQ2Q_ehAd-d_o0ME/edit?usp=sharing
Any help is appreciated!
non-scripted solution:
=IFERROR({INDEX(IFERROR(Data!A1:G1/0)); Data!A1:G1; QUERY({Data!A2:G}, "where 1=1 "&
IF(C10="",,"and lower(Col1) contains '"&LOWER(C10)&"'")&
IF(C12="",,"and Col2 = "&C12)&
IF(C14="",,"and lower(Col3) contains '"&LOWER(C14)&"'")&
IF(C16="",,"and lower(Col4) contains '"&LOWER(C16)&"'")&
IF(C18="",,"and lower(Col5) contains '"&LOWER(C18)&"'")&
IF(C20="",,"and lower(Col6) contains '"&LOWER(C20)&"'")&
IF(C22="",,"and lower(Col7) contains '"&LOWER(C22)&"'"), 0)}, {"";"no data"})
demo sheet
Here is third variant of the script:
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
function onLoad() { reset() }
function reset() {
SS.toast('Please wait...');
SHEET_USERFACE.getRange('c9:c21').clearContent();
SHEET_USERFACE.getRange('c9:c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_prop(obj);
update_menu_unit(obj);
update_menu_type(obj);
SS.toast('The sheet has been reset');
}
function onEdit(e) {
if (e.range.getSheet().getName() != 'Userface') return;
if (e.range.columnStart != 3) return;
// Property menu
if (e.range.rowStart == 9) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c11:c21').clearContent();
SHEET_USERFACE.getRange('c11:c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_unit(obj);
update_menu_type(obj);
e.source.toast('The sheet has been updated');
}
// Apt/Unit menu
if (e.range.rowStart == 11) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c13:c21').clearContent();
SHEET_USERFACE.getRange('c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_type(obj);
e.source.toast('The sheet has been updated');
}
// Applicance type menu
if (e.range.rowStart == 13) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c15:c21').clearContent();
var obj = make_obj_from_data();
update_brand_model_serial_color(obj);
e.source.toast('The sheet has been updated');
}
}
function make_obj_from_data() {
var data = SHEET_DATA.getDataRange().getValues().slice(1);
var obj = {};
for (let row of data) {
var [prop, unit, type, ...etc] = row;
try {
obj[prop][unit][type] = etc;
}
catch(e) {
try {
obj[prop][unit] = {}; obj[prop][unit][type] = etc;
}
catch(e) {
obj[prop] = {}; obj[prop][unit] = {}; obj[prop][unit][type] = etc;
}
}
}
return obj;
}
function update_menu_prop(obj) {
var cell = SHEET_USERFACE.getRange('c9');
try {
var list = Object.keys(obj);
set_data_validation(cell, list);
} catch(e) {
console.log('update_menu_prop(obj)');
console.log(e);
}
}
function update_menu_unit(obj) {
var prop = SHEET_USERFACE.getRange('c9').getValue();
var cell = SHEET_USERFACE.getRange('c11');
try {
var list = Object.keys(obj[prop]);
set_data_validation(cell, list);
} catch(e) {
console.log('update_menu_unit(obj)');
console.log(e);
}
}
function update_menu_type(obj) {
var prop = SHEET_USERFACE.getRange('c9').getValue();
var unit = SHEET_USERFACE.getRange('c11').getValue();
var cell = SHEET_USERFACE.getRange('c13');
try {
var list = Object.keys(obj[prop][unit]);
set_data_validation(cell, list);
if (list.length == 1) update_brand_model_serial_color(obj)
} catch(e) {
console.log('update_menu_type(obj)');
console.log(e);
}
}
function update_brand_model_serial_color(obj) {
var [prop,,unit,,type] = SHEET_USERFACE.getRange('c9:c13').getValues();
try {
var [brand, model, serial, color] = obj[prop][unit][type];
var arr = [[brand],[''],[model],[''],[serial],[''],[color]];
SHEET_USERFACE.getRange('c15:c21').setValues(arr);
} catch(e) {
console.log('update_brand_model_serial_color(obj)');
console.log(e);
}
}
function set_data_validation(cell, list) {
var rule = SpreadsheetApp.newDataValidation().requireValueInList(list).build();
cell.setDataValidation(rule);
// put the value in the cell if there is just one element in the list
if (list.length == 1) cell.setValue(list[0]);
}
Here is my sheet.
It works about that way as it does any similar interface. You select the first menu and it changes data validation for the second menu and cleans the third menu. Then you select the second menu and it changes the third one. As soon as you change the third menu it fills the rest fields.
Since you're using just the three menus and they supposed to be changed step by step I decided to 'hardcode' them. It's not the best practice and there can be problems if/when you decide to change the functionality. But for this particular case I think the 'hardcoding' is forgivable. It works relatively fast and the code is relatively readable.
Just for fun I've made it. But this is overkill:
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
function onLoad() { reset() }
function onEdit(e) {
if (e.range.getSheet().getName() != 'Userface') return;
if (e.range.columnStart != 3) return;
if (![9,11,13,15,17,19,21].includes(e.range.rowStart)) return;
e.source.toast('Please, wait...');
set_filter(e.range.offset(0,-1).getValue(), e.value);
set_all_menus();
e.source.toast('The sheet has been updated');
}
function reset() {
SS.toast('Please wait...');
try { SHEET_DATA.getFilter().remove() } catch(e) {}
SHEET_USERFACE.getRange('c9:c21').clearContent().clearDataValidations();
set_all_menus();
SS.toast('The sheet has been updated');
}
function set_all_menus() {
var data = SHEET_DATA.getDataRange().getDisplayValues().filter((_,i) => !SHEET_DATA.isRowHiddenByFilter(i+1));
set_menu(data, 'b9', 'c9');
set_menu(data, 'b11', 'c11');
set_menu(data, 'b13', 'c13');
set_menu(data, 'b15', 'c15');
set_menu(data, 'b17', 'c17');
set_menu(data, 'b19', 'c19');
set_menu(data, 'b21', 'c21');
}
function set_menu(data, title, cell) {
var menu_title = SHEET_USERFACE.getRange(title).getValue();
var menu_cell = SHEET_USERFACE.getRange(cell);
var col_index = data[0].indexOf(menu_title);
var menu_list = [...new Set([...data.map(e => e[col_index])])].slice(1);
var menu_rule = SpreadsheetApp.newDataValidation().requireValueInList(menu_list).build();
menu_cell.setDataValidation(menu_rule);
}
function set_filter(column_title, value) {
// get all the data and col index
var [header, ...data] = SHEET_DATA.getDataRange().getValues();
var col_index = header.indexOf(column_title);
// unhide all values of the given column
var clear = SpreadsheetApp.newFilterCriteria().setHiddenValues([]).build();
var range = SHEET_DATA.getDataRange();
var filter = range.getFilter() || range.createFilter()
filter.setColumnFilterCriteria(col_index+1, clear);
// get the values to hide
var col_data = data.map(e => e[col_index]);
var filtered = col_data.filter( (e, i) => e != value && SHEET_DATA.isRowHiddenByFilter(i+1) );
var to_hide = col_data.filter( e => e != value );
var hidden = [...new Set([...filtered, ...to_hide])];
// hide the values with the filter
var criteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(hidden).build();
var range = SHEET_DATA.getDataRange();
var filter = range.getFilter() || range.createFilter()
filter.setColumnFilterCriteria(col_index+1, criteria);
}
Here is the sheet.
It works quite slow. I'd propose to use the native filters instead. Basically the script turns on and off the filters an changes data validation for the dropdown menus respectively.
Update
Here another version of the script. It works much faster but it uses the 'helper sheet' to store temporary data (the filtered table). You can hide the 'helper sheet' if you want.
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
var SHEET_HELPER = SS.getSheetByName('Helper'); // the hidden sheet with temp data
var PROPERTY_LIST = [...new Set(SHEET_DATA.getRange('a2:a').getValues().flat())]; // 'Property' list
var DATA_OBJ = {};
function onLoad() { reset() }
function onEdit(e) {
var {range, source, value} = e;
if (range.getSheet().getName() != 'Userface') return;
if (range.columnStart != 3) return;
if (![9,11,13,15,17,19,21].includes(range.rowStart)) return;
source.toast('Please, wait...');
// reset whenever the first menu is changing
if (range.rowStart == 9) {
reset();
source.getRange('c9').setValue(value);
}
var col_header = range.offset(0,-1).getValue();
update_sheet_helper(col_header, value);
update_all_dropdown_menus();
source.toast('The sheet has been updated');
}
function reset() {
SS.toast('Please wait...');
// copy data from SHEET_DATA to SHEET_HELPER
SHEET_USERFACE.getRange('c9:c21').clearContent().clearDataValidations();
SHEET_DATA.getDataRange().copyTo(SHEET_HELPER.clearContents().getRange(1,1));
update_data_obj();
update_all_dropdown_menus();
SS.toast('The sheet has been updated');
}
// make DATA_OBJECT from SHEET_HELPER
function update_data_obj() {
DATA_OBJ = {};
var [header, ...data] = SHEET_HELPER.getDataRange().getValues();
for (let i in header) DATA_OBJ[header[i]] = data.map(e => e[i]);
DATA_OBJ['Property'] = PROPERTY_LIST; // let 'Property' list will be full always
}
// remove from SHEET_DATA_HELPER all the rows
// that have no given value in column with given title
function update_sheet_helper(col_title, value) {
var [header, ...data] = SHEET_HELPER.getDataRange().getValues();
var col_index = header.indexOf(col_title);
data = data.filter(k => k[col_index] == value);
var table = [header, ...data];
SHEET_HELPER.clearContents().getRange(1,1,table.length, table[0].length).setValues(table);
update_data_obj();
}
function update_all_dropdown_menus() {
SHEET_USERFACE.getRange('b9:c21').getValues().forEach((row,i) => {
if (row[0] != '') set_data_validation(DATA_OBJ[row[0]], 'c' + (i+9));
});
function set_data_validation(data, cell_address) {
var menu_list = [...new Set([...data])]; // remove duplicates from the array
var menu_rule = SpreadsheetApp.newDataValidation().requireValueInList(menu_list).build();
var cell_range = SHEET_USERFACE.getRange(cell_address)
cell_range.setDataValidation(menu_rule);
if (menu_list.length == 1) cell_range.setValue(menu_list[0]);
}
}
The sheet is here.

How can I check if a numerical value is within a range of cells in google sheets?

I would like to find if a certain value is in a range using app scripts for google sheets.
var sheet = SpreadsheetApp.getActiveSheet();
var rangeBikeNumbers = sheet.getDataRange("A5:A5000");
var values = rangeBikeNumbers.getValues();
If I have my range rangeBikeNumbers, how can I check if the number "42" for example is in that range. I have searched for hours now and have beeb unable to find any answer to this. indexOf only seems to return -1, regardless of whether or not the value is in the range.
var indexDataNumber = values.indexOf(42); for example always ends up being -1
I believe your goal as follows.
You want to check whether the value of 42 is existing in the range of A5:A5000.
In this case, I would like to propose to use TextFinder. Because when TexiFinder is used, the process cost is low. Ref By the way, getDataRange has not arguments. From your script, I thought that you might want var rangeBikeNumbers = sheet.getRange("A5:A5000");.
When this is reflected to your script, it becomes as follows.
Modified script:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rangeBikeNumbers = sheet.getRange("A5:A5000");
var find = rangeBikeNumbers.createTextFinder("42").matchEntireCell(true).findNext();
if (find) {
// In this case, the value of 42 is existing in the range.
} else {
// In this case, the value of 42 is NOT existing in the range.
}
}
Note:
About var indexDataNumber = values.indexOf(42); for example always ends up being -1, I think that the reason of this issue is due to that values is 2 dimensional array. If you want to use this, you can also use the following script.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rangeBikeNumbers = sheet.getRange("A5:A5000");
var values = rangeBikeNumbers.getValues();
var find = values.map(([e]) => e).indexOf(42); // of values.flat().indexOf(42);
if (find > -1) {
// In this case, the value of 42 is existing in the range.
} else {
// In this case, the value of 42 is NOT existing in the range.
}
}
References:
Benchmark: Process Costs for Searching Values in Spreadsheet using Google Apps Script
getDataRange()
getRange(a1Notation)
createTextFinder(findText)
Select any active range that you wish to search and it will search for the seed in that at range. The seed is currently defaulted to 42 but you can change it.
function findSeedInRange(seed = 42) {
const ui = SpreadsheetApp.getUi();
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const rg = sh.getActiveRange();
const row = rg.getRow();
const col = rg.getColumn();
var found = false;
rg.getValues().forEach((r, i) => {
r.forEach((c, j) => {
if (c == seed) {
let r = sh.getRange(i + row, j + col).getA1Notation();
ui.alert(`Found ${seed} in ${r}`);
found = true;
}
})
})
if(!found) {
ui.alert(`Did not find ${seed}`);
} else {
ui.alert('That is all.')
}
}
Here's another approach:
function findSeedInRange() {
const ui = SpreadsheetApp.getUi();
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const rg = sh.getActiveRange();
const resp = ui.prompt('Enter Seed', 'Enter Seed', ui.ButtonSet.OK_CANCEL)
if (resp.getSelectedButton() == ui.Button.OK) {
var seed = parseInt(resp.getResponseText());
const row = rg.getRow();
const col = rg.getColumn();
var found = false;
rg.getValues().forEach((r, i) => {
r.forEach((c, j) => {
if (c == seed) {
let r = sh.getRange(i + row, j + col).getA1Notation();
ui.alert(`Found ${seed} in ${r}`);
found = true;
}
});
});
if (!found) {
ui.alert(`Did not find ${seed}`);
} else {
ui.alert('That is all.')
}
} else {
ui.alert('Operation cancelled.')
}
}

Pivot or Transpose data in a google sheet using apps script

I'm hoping someone can assist me with either pivoting or transposing data within a google sheet programmatically using apps script.
Below is what I've done so far. I'm pretty sure this is far from the correct/optimum way of achieving this hence me reaching out here. The data is for a survey with 3 questions. The script runs fine but doesn't account for any surveys where there where no answers selected for a certain question or for any one of the 3 questions. I'm pretty sure that's because there needs to be an else statement part of each If statement but can't figure out how to write that logic within each loop.
I also tried experimenting by converting this to and array of objects, where I tried using the ID key to to match the date, source, and three questions but couldn't get that working either.
I've attached images of what the source data looks like and what I'm trying to achieve, as well as what it currently looks like after I execute the script I currently have.
The code I've written is after the 3 images I've uploaded.
Hope I've explained this all correctly. I'd appreciate any assistance with this.
The source data looks like this:
And this is what I am trying to achieve:
This is what the data looks like once the above code has run:
function sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('sampleData');
var range = sheet.getRange(2,1,sheet.getLastRow()-1,sheet.getLastColumn());
var values = range.getValues();
var resultsSh = ss.getSheetByName('sampleResults');
//console.log(values);
var source = values.filter(function(row){
if(row[2] === 'Source'){
return true;
} else {
return false;
}
});
//console.log(source);
var q1 = values.filter(function(row){
if(row[2] === 'Question1'){
return true;
} else {
return false;
}
});
//console.log(q1);
var q2 = values.filter(function(row){
if(row[2] === 'Question2'){
return true;
} else {
return false;
}
});
//console.log(q2);
var q3 = values.filter(function(row){
if(row[2] === 'Question3'){
return true;
} else {
return false;
}
});
//console.log(q3);
var result1 = [];
for (i=0;i<source.length;i++){
for (j=0;j<q1.length;j++){
if(source[i][1] === q1[j][1]){
result1.push([...source[i], ...q1[j]]);
}
}
}
//console.log(result1);
var result2 = [];
for (i=0;i<result1.length;i++){
for (j=0;j<q2.length;j++){
if (result1[i][1] === q2[j][1]) {
result2.push([...result1[i],...q2[j]])
}
}
}
//console.log(result2);
var final = [];
for (i=0;i<result2.length;i++) {
for (j=0;j<q3.length;j++) {
if (result2[i][1] === q3[j][1]) {
final.push([...result2[i], ...q3[j]])
}
}
}
//console.log(final);
var data = final.map(function(row){
return [row[0].toLocaleString('en-GB').replace(/[',']/g,''), row[1], row[3], row[7], row[11], row[15], row[7] + row[11] + row[15]];
});
console.log(data);
ss.getSheetByName('Sheet16').getRange(2, 1, data.length, data[0].length).setValues(data);
}
This can be done with a single for loop that checks each row and a switch() case: to fill up the respective array elements and compute for the sum.
function sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('sampleData');
var range = sheet.getRange(2,1,sheet.getLastRow()-1,sheet.getLastColumn());
var values = range.getValues();
var resultsSh = ss.getSheetByName('sampleResults');
var lastRow = sheet.getLastRow();
var lastID = values[0][1];
var result = [];
var sum = 0;
// check each row
for (i=0; i<lastRow-1; i++) {
var currID = values[i][1];
if ((currID != lastID) & sum) {
result[result.length-1] = sum;
}
switch (values[i][2]) {
case 'Source':
result.push(values[i][0],values[i][1],values[i][3],'','','','');
sum = 0;
break;
case 'Question1':
result[result.length-4] = values[i][3];
sum += values[i][3];
break;
case 'Question2':
result[result.length-3] = values[i][3];
sum += values[i][3];
break;
case 'Question3':
result[result.length-2] = values[i][3];
sum += values[i][3];
break;
}
lastID = currID;
}
result[result.length-1] = sum;
// convert to 2d array
const result2d = [];
while(result.length) result2d.push(result.splice(0,7));
// put to results sheet
var resultRange = resultsSh.getRange(2,1,result2d.length,result2d[0].length);
resultRange.setValues(result2d);
}
Sample Data:
Sample Results:

How can I make a Google Sheet script run reliably on Samsung tablets?

I have two functions in my Google Sheet script which are each triggered by means of a checkbox (since Google Sheets on mobile can't use images as buttons). They work on PC (rather slowly), but on tablets, they tend to fail more often than not, which then also affects PC users.
The script is set up to perform an onEdit check of two checkbox cells. If the checkbox in cell C3 is checked, the AUTOFILL function should run (which displays the A cell value of the last row on the Info sheet plus 1 in cell C4 of the Data Entry sheet, and then clears the checkbox), and if the checkbox in cell C12 is checked, the SUBMIT function should run (which takes the range of data entered on the Data Entry sheet and updates an existing row/adds a new row on the Data sheet with the information from the Data Entry sheet, adding a timestamp if cell C11 on the Data Entry sheet contains the word 'CLEANED', and then clears the checkbox).
I've tried experimenting with various WIFI signal strengths and more powerful tablets, but I am unable to pinpoint the exact culprit here - sometimes this will run, most often the checkbox will just remain checked and nothing happens. Laptops and desktop computers all seem to run, but if a tablet tries to run and fails, the computers will sometimes not run either, until I go into the script itself and manually force once of the functions to run, which seems to reset things and lets the computers work again.
Is it because of the processing required in order to run this code? I've tried to optimize it as much as possible, but is there something else that I might change here which would make this work, every time?
Here is the example sheet, and here is the script:
function onEdit(e) {
if (e.range.getSheet().getName() != "Data Entry") {
return
}
var isAutofill = SpreadsheetApp.getActiveSheet().getRange("C3").getValue();
var isSubmit = SpreadsheetApp.getActiveSheet().getRange("C12").getValue();
if (isAutofill && isSubmit) {
Browser.msgBox("You cannot autofill and submit data at the same time!");
SpreadsheetApp.getActiveSheet().getRange("C3").setValue(false);
SpreadsheetApp.getActiveSheet().getRange("C12").setValue(false);
} else if (isAutofill) {
AUTOFILL();
SpreadsheetApp.getActiveSheet().getRange("C3").setValue(false);
} else if (isSubmit) {
SUBMIT();
SpreadsheetApp.getActiveSheet().getRange("C12").setValue(false);
}
}
function AUTOFILL() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Info');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data Entry');
var valueOfData = sheet1.getRange(sheet1.getLastRow(), 1).getValue();
sheet2.getRange('C4').setValue(valueOfData + 1);
}
function SUBMIT() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Data Entry");
var dataSheet = ss.getSheetByName("Info");
var values = formSS.getRange("C4:C11").getValues().reduce(function(a, b) {
return a.concat(b)
});
var partNum = values[0];
var row;
dataSheet.getDataRange().getValues().forEach(function(r, i) {
if (r[0] === partNum) {
row = i + 1
}
})
row = row ? row : dataSheet.getLastRow() + 1;
var data = dataSheet.getRange(row, 1, 1, 8).getValues()[0].map(function (el, ind){
return el = values[ind] ? values[ind] : el;
})
var statusValue = formSS.getRange("C11").getValue();
if (statusValue != 'CLEANED') {
dataSheet.getRange(row, 1, 1, 8).setValues([data]);
}
if (statusValue == 'CLEANED') {
var now = [new Date()];
var newData = data.concat(now)
dataSheet.getRange(row, 1, 1, 9).setValues([newData]);
}
formSS.getRange("C4:C11").clearContent()
}
I made a few changes. Take a look. Hopefully you can use them to speed up the function.
function onEdit(e) {
var sh=e.range.getSheet();
if (sh.getName() != "Data Entry") {return;}
var rgC3=SpreadsheetApp.getActiveSheet().getRange("C3");
var rgC12=SpreadsheetApp.getActiveSheet().getRange("C12");
var isAutofill = rgC3.getValue();
var isSubmit = rgC12.getValue();
if (isAutofill && isSubmit) {
e.source.toast("You cannot autofill and submit data at the same time!");
rgC3.setValue(false);
rgC12.setValue(false);
} else if (isAutofill) {
AUTOFILL(e.source);
rgC3.setValue(false);
} else if (isSubmit) {
SUBMIT(e.source);
rgC12.setValue(false);
}
}
function AUTOFILL(ss) {
var sheet1 = ss.getSheetByName('Info');
var sheet2 = ss.getSheetByName('Data Entry');
var valueOfData = sheet1.getRange(sheet1.getLastRow(), 1).getValue();
sheet2.getRange('C4').setValue(valueOfData + 1);
}
function SUBMIT(ss) {
var formSS=ss.getSheetByName("Data Entry");
var dataSheet=ss.getSheetByName("Info");
var values=formSS.getRange("C4:C11").getValues().reduce(function(a, b) {return a.concat(b)});
var partNum = values[0];
var row;
var data=dataSheet.getDataRange().getValues()
for(var i=0;i<data.length;i++) {
if(data[i][0]==partNum) {
row=i+1;
break;
}
}
row = row ? row : dataSheet.getLastRow() + 1;
var data = dataSheet.getRange(row, 1, 1, 8).getValues()[0].map(function (el, ind){return el = values[ind] ? values[ind] : el;})
var statusValue = formSS.getRange("C11").getValue();
if (statusValue != 'CLEANED') {dataSheet.getRange(row, 1, 1, 8).setValues([data]);}
if (statusValue == 'CLEANED') {var now = [new Date()];var newData=data.concat(now);dataSheet.getRange(row, 1, 1, 9).setValues([newData]);}
formSS.getRange("C4:C11").clearContent();
}

Google Sheets formula to determine if text in a cell is italic

Is it possible in Google Sheets to run a formula to determine if the text in a cell is italic?
Something like isItalic(a1) which would return TRUE / FALSE, so that the column could be filtered on?
You could also do something like the following in Google Sheets.
The .getFontStyle() method might be what you're looking for.
var ID = 'yourDocumentID';
var name = 'nameOfYourSheet';
function myFunction() {
var sourceSheet = SpreadsheetApp.openById(ID); // Selects your Source Spreadsheet by its ID
var ssheet = sourceSheet.getSheetByName(name); // Selects your Source Sheet by its Name
var range = ssheet.getRange(6, 10) // Selects J6 (just as an example)
var fontStyle = range.getFontStyle(); // Tells you the font style
ssheet.getRange(1,1).setValue(fontStyle); // writes the font style into A1
var returnBoolean = range.getFontStyle() === 'italic' ? true : false; // checks if getFontStyle is true or false
ssheet.getRange(2,1).setValue(returnBoolean); // writes the boolean into A2
}
A link to the docs can be found here.
=CHECKSTYLE(A1, "italic")
function CHECKSTYLE(range, styleElement) {
var arr = [],
ret;
var styleEl = ["line-through", "underline", "italic", "bold"]
var range = SpreadsheetApp.getActiveSheet()
.getRange(range);
var styles;
if (styleEl.indexOf(styleElement) == 2) {
styles = range.getFontStyles()
} else if (styleEl.indexOf(styleElement) == 3) {
styles = range.getFontWeights()
} else if (styleEl.indexOf(styleElement) != -1) {
styles = range.getFontLines();
} else {
throw "the 2nd parameter can only be " + styleEl.toString()
.replace(",", ", ")
}
styles.reduce(function (a, b) {
return a.concat(b);
})
.forEach(function (el) {
el === styleElement ? arr.push(["TRUE"]) : arr.push(["FALSE"])
});
range.getNumRows() == 1 ? ret = transpose(arr) : ret = arr;
return ret;
}
// In cell place =ISSTYLE((CELL("address",D4)&":"&CELL("address",D4)), "italic")
function ISSTYLE(pRange, styleElement) {
// thanks to Https://stackoverflow.com/questions/54308576/google-sheets-formula-to-determine-if-text-in-a-cell-is-italic
// thanks to https://webapps.stackexchange.com/questions/10629/how-to-pass-a-range-into-a-custom-function-in-google-spreadsheets/92156
var range = SpreadsheetApp.getActiveSpreadsheet().getRange(pRange);
return range.getFontStyle() == styleElement;
}