Checking two columns and grouping to validate entries - google-apps-script

I have data sheet as below-
I am trying to check for values in column Count of deletions if it is empty or not a number to flag an error at an account level,
because there will be only one entry of Count of deletions which will be at the first row entry of Account ID (unique) as illustrated in the pic above.
I did this to just validate the column Count of deletions
function myFunction() {
SS = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SS.getSheetByName("Sheet1");
var Validation_sheet = SS.getSheetByName("Sheet2");
var last_row = sheet.getLastRow();
var values = sheet.getRange(2,5,last_row).getValues();
var Status_values = sheet.getRange(2,4,last_row).getValues();
var result = isnumberorEmpty(values, Status_values);
const Count_setvalue = Validation_sheet.getRange("A2");
var errors_c = [];
for (key in result){
var check1 = result[key] == "empty";
var check2 = result[key] == "Not a number";
if(check1 == true || check2 == true){
errors_c.push(key);
}
}
if (errors_c.length > 0){
Count_setvalue.setValue("Row(s) " + errors_c.join(" and ") + " have error");
}
else{
Count_setvalue.setValue("No errors");
}
}
function isnumberorEmpty(array, array1) {
let result = {};
for (let i = 0; i < array.length-1; i++) {
if( array1[i] == "Completed"){
let row = array[i];
if (row[0] === "") {
result[i+2] = "empty";
}
else if (row[0] === "Ignored") {
result[i+2] = "No error";
}
else if (!isNaN(row[0])) {
result[i+2] = "number";
}
else {
result[i+2] = "Not a number";
}
}
else{
}
}
return result;
}
The above checks for column Count of deletions and validates only where corresponding rows in column Status has Completed.
The output will show me error for rows, 8 and 10-18,
but it should not as the values are entered at the account level.
However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -

From your updated question, I thought that the reason for your current issue might be due to that you checked columns "D" and "E". In order to achieve However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -, how about checking the columns "A", "D" and "E"? In this case, how about the following sample script?
Sample script 1:
function myFunction1() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const { errors_c } = values.reduce((o, r, i) => {
if (i == 0 || o.temp != r[0]) {
o.temp = r[0];
if (r[3] == "Completed" && !r[4]) {
o.errors_c.push(i + 2);
}
}
return o;
}, { errors_c: [], temp: "" });
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
In this modification, the columns "A", "D" and "E" are checked.
When this script is tested to your sample input Spreadsheet, Row(s) 16 and 18 have error is put to the cell "A2" of "Sheet2".
Sample script 2:
Although unfortunately, I'm not sure whether I could correctly understand your expected result, if you want to check all values of column "D" for each "Account ID", how about the following sample script?
function myFunctionc2() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const obj = values.reduce((m, r, i) => m.set(r[0], m.has(r[0]) ? { v: [...m.get(r[0]).v, r[3]], i: m.get(r[0]).i } : { v: [r[3]], i: i + 2 }), new Map()).values();
const errors_c = [...obj].reduce((ar, { v, i }) => {
if (v.every(e => e == "Completed") && !values[i - 2][4]) {
ar.push(i);
}
return ar;
}, []);
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
Reference:
reduce()

Related

how do add an additional filter function in google appscript to reference a column and searching for text "TMO"

Here is the code I have for searching by a specific color. I would like to then like to also filter by the word TMO in column K
function TmoSuspended () {
var ss = SpreadsheetApp.getActive().getSheetByName("Boost Prod Account Info")
/*var cell_color = ss.getRange("A4").getBackground(); #fff2cc
Logger.log(cell_color);*/
var last_row = ss.getLastRow();
var array = ss.getRange(1, 1, last_row).getBackgrounds();
var allRows = ss.getRange("A:A");
ss.unhideRow(allRows);
for (i=8 ;i <= last_row;i++){
if(array[i] != "#f1c232")
{
ss.hideRows(i+1)};
}
}
As what I have understood from your concern, you may try this code:
This will hide rows with color #f1c232 in column A and "TMO" word in column K.
Try:
function TmoSuspended() {
var ss = SpreadsheetApp.getActive().getSheetByName("Boost Prod Account Info")
var array = ss.getRange("A:A").getBackgrounds();
//Checker of color (""#f1c232"") on column A and return true if not equal to blanks and word 'TMO'
try { var res = array.map((color, index) => { return color[0] == "#f1c232" ? [true, ss.getRange('K:K').getValues().map(x => { return x != '' && x.toString().match(/TMO/gm) })[index]].flat() : false }) } catch { }
//This will return rows that are not equal to #f1c232 and "TMO"
console.log(res.map((x,index) => {return x[0] == true && x[1] == "TMO" ? ss.hideRows(index+1) : null}))
}
Let me know if this helps.

Delete and add columns in google sheet

I am using the below code to create the validations in google sheet (contributed by Cooper), what this script does is it automatically check the applicable headers and create the dropdown with values and hide the columns which are not applicable.
I am trying to solve here is:
The script checks the applicable headers related to the Product Selection
It creates the dropdown with validation values
Instead of hiding the not applicable columns, It removes them from the sheet
I am a beginner to google script and tried using the deletecolumn function but unable to get it work.
Please help me out here.
function loadObjectsAndCreateProductDropDown() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const psh = ss.getSheetByName('Sheet1');
const [ph, ...prds] = sh.getRange(1, 1, 10, 6).getValues().filter(r => r[0]);
const [ch, ...chcs] = sh.getRange(11, 1, 10, 10).getValues().filter(r => r.join());
let pidx = {};
ph.forEach((h, i) => { pidx[h] = i });
let prd = { pA: [] };
prds.forEach(r => {
if (!prd.hasOwnProperty(r[0])) {
prd[r[0]] = { type: r[pidx['Type']], size: r[pidx['Size']], color: r[pidx['Color']], material: r[pidx['Material']], length: r[pidx['Length']] };
prd.pA.push(r[0]);
}
});
let cidx = {};
let chc = {};
ch.forEach((h, i) => { cidx[h] = i; chc[h] = [] });
chcs.forEach(r => {
r.forEach((c, i) => {
if (c && c.length > 0) chc[ch[i]].push(c)
})
})
const ps = PropertiesService.getScriptProperties();
ps.setProperty('product_matrix', JSON.stringify(prd));
ps.setProperty('product_choices', JSON.stringify(chc));
Logger.log(ps.getProperty('product_matrix'));
Logger.log(ps.getProperty('product_choices'));
psh.getRange('A2').setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(prd.pA).build());
}
//I chose to use an installable dropdown. I'm not sure if it's needed. Its up to you.
function onMyEdit(e) {
//e.source.toast('entry')
const sh = e.range.getSheet();
if (sh.getName() == 'Sheet1' && e.range.columnStart == 1 && e.range.rowStart == 2 && e.value) {
//e.source.toast('flag1');
sh.getRange('C2:G2').clearDataValidations();
let ps = PropertiesService.getScriptProperties();
let prodObj = JSON.parse(ps.getProperty('product_matrix'));//recovering objects from PropertiesService
let choiObj = JSON.parse(ps.getProperty('product_choices'));
let hA = sh.getRange(1, 1, 1, sh.getLastColumn()).getDisplayValues().flat();
let col = {};
hA.forEach((h, i) => { col[h.toLowerCase()] = i + 1 });
["type", "size", "color", "material", "length"].forEach(c => {
if (choiObj[prodObj[e.value][c]]) {
sh.getRange(e.range.rowStart, col[c]).setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(choiObj[prodObj[e.value][c]]).build()).offset(-1,0).setFontColor('#000000');
sh.showColumns(col[c])
} else {
sh.getRange(e.range.rowStart, col[c]).offset(-1,0).setFontColor('#ffffff');
sh.hideColumns(col[c]);
}
})
}
}
demo sheet
https://docs.google.com/spreadsheets/d/18guAXXjWIMDQilX8Z0Y4_Avogjs2ESMbrZY7Sb9TaxE/edit#gid=0
Sample Data
P1
Type
Size
Color
Material
Length
Kurta Pyjamas
No
Sizeethnic_1
Colorethnic_1
Materialethnic_3
Lengthethnic_1
Dhotis
Typethnic_1
No
Colorethnic_2
Materialethnic_2
No
Sherwani
No
No
Colorethnic_2
No
Lengthethnic_2
Men Pyjamas
Typeethnic_2
No
Colorethnic_2
No
No
Kurtas
No
Sizeethnic_2
Colorethnic_1
No
Lengthethnic_1
Ethnic Jackets
No
No
Colorethnic_1
No
No
Typethnic_1
Typeethnic_2
Sizeethnic_1
Sizeethnic_2
Colorethnic_1
Colorethnic_2
Materialethnic_3
Materialethnic_2
Lengthethnic_1
Lengthethnic_2
Mundu
Churidar
XS
XS
Beige
Green
Blended
Silk Blend
Above Knee
Short
Regular Dhoti
Regular Pyjama
S
S
Black
Grey
Cotton
Velevt
Ankle Length
Medium
Patiala
M
M
Blue
Maroon
Dupion
Viscose Rayon
Jodhpuri
L
L
Brown
Multi
Wool
Harem
XL
XL
Copper
Mustard
XXL
XXL
Cream
3XL
3XL
Gold
Suggestion
This may not be the cleanest code but you may try this implementation below. Instead of removing columns, it will clear the Sheet1 headers and their corresponding drop-downs on every new selection on the A2 drop-down.
NOTE: Since your sample data will increase in size overtime, this setup will need you to put the data into a separate sheet tab for a cleaner setup, such as this sample below:
Data1 sheet tab:
Data2 sheet tab:
UPDATED
Sample Script
var sh = SpreadsheetApp.getActiveSpreadsheet();
var selection = sh.getSheetByName("Sheet1").getRange("A2").getValue(); //Get the selection on the dropdowm on cell A2
var data1 = sh.getSheetByName("Data1").getDataRange().getDisplayValues();
var data2 = sh.getSheetByName("Data2").getDataRange().getDisplayValues();
function addHeaders(sheet, values) { //Adds the headers from Column C and beyond
var startCol = 3; //Column C
var endCol = startCol + values.length;
values.forEach(x => {
if(startCol <= endCol){
if(checkHeaderIfYesOrNo(x) == true)return;
sheet.getRange(1,startCol).setValue(x);
startCol += 1;
}
});
}
function onEdit(e) {
if(e.range.getA1Notation() != "A2")return;//Make sure to run onEdit function ONLY when cell A2 is edited/selected
var headers = [];
var headerValues = [];
var temp = [];
var counter = 0;
clean();
data1 = fixDuplicates();
//find selection name on data1
for(var x = 0; x< data1.length; x++){
var name = data1[x][0];
if(name == selection){
///get the headers & their values
data1[x].forEach(res => {
if(res != "" & res != selection){
var index1 = data1[x].indexOf(res);
var index2 = data2[0].indexOf(res);
headers.push([data1[0][index1]]);
for(var y=0; y< data2.length; y++){
if(data2[y][index2] != "" && data2[y][index2] != res){
temp.push("**"+res+"**"+data2[y][index2]); //place raw header data to a temporary variable
}
}
//Set the drop-down data of each headers
temp.forEach(raw => { //clean the temp array
if(raw.includes(res)){
var regex = /\*\*([^*]*(?:\*(?!\*)[^*]*)*)\*\*/g;
headerValues.push(raw.replace(regex, ""))
}
});
//Logger.log("Data of the \""+res+"\" header:\n"+headerValues);
//set data validation per header
counter += 1;
if(res.toLowerCase().includes("no"))return; //skip creating data validation for "No" header
if(res.toLowerCase().includes("yes")) return; //skip creating data validation for "Yes" header
sh.getSheetByName("Sheet1").getRange(2,2+counter).setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(headerValues).build());
headerValues = [];
}
});
}
}
addHeaders(sh.getSheetByName("Sheet1"), headers);
}
function clean(){ //Clean Sheet 1 on every edit
sh.getSheetByName("Sheet1").getRange('C2:Z').clearDataValidations();
sh.getSheetByName("Sheet1").getRange('C1:Z').clearContent();
}
function fixDuplicates(){
var temp = [];
var data1New = [];
var count = 1;
for(var x=0; x<data1.length; x++){
data1[x].forEach(findIt => {
if(findIt.toLowerCase().includes("yes") || findIt.toLowerCase().includes("no")){
temp.push(findIt+count);
count += 1;
}else{
temp.push(findIt);
}
})
data1New.push(temp);
temp=[];
}
return data1New;
}
function checkHeaderIfYesOrNo(h){
for(var x=0; x<data1.length; x++){
if(data1[x][0] == selection){
if(data1[x][data1[0].indexOf(h.toString())].toLowerCase().includes("yes")){
Logger.log(h +" contains Yes");
return null;
}else if(data1[x][data1[0].indexOf(h.toString())].toLowerCase().includes("no")){
Logger.log(h+" header will not be added as it has \"No\" value");
return true;
}else{
Logger.log("Skip the "+h +" header");
return null;
}
}
}
}
Sample Demonstration:
Sample Execution Log result for review:

else statement does not return text "ID not found" google apps script

Return works if there is a match but if there is no match, the else statement doesn't work. The whole code is pasted below
function doGet(e){
return getAirportMatch(e) ;
}
function getAirportMatch(e) {
var id = e.parameter.id;
var regExp ="^.*"+id+".*|\\s{1}"+id+".*$//gm";
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1ggLOdIUES107tr_r8R-EAJpbRmZSty8JdXQJfb2U2tU/edit#gid=0");
var sheet = ss.getSheetByName("Sheet1");
var lastRow = sheet.getLastRow();
var lookupRangeValues = sheet.getRange(1, 1, lastRow, 1).getValues();
let a = []
lookupRangeValues.forEach(function (row, i){
if (row[0].match(regExp))
{
a.push(lookupRangeValues[i][0]);
}
});
if (a)return ContentService.createTextOutput(a).setMimeType(ContentService.MimeType.TEXT)
else return ContentService.createTextOutput("ID not found").setMimeType(ContentService.MimeType.TEXT);
You never enter the else statement
Reason:
Even if a is empty, it is still defined and thus the condition if (a) is always fullfilled.
You can verify this easily my modifying your text output and / or implementing logs.
Sample:
function getAirportMatch(e) {
...
let a = []
lookupRangeValues.forEach(function (row, i){
if (row[0].toString().match(regExp))
{
a.push(lookupRangeValues[i][0]);
}
});
console.log("a: " + a);
console.log("a.length: " + a.length);
if (a){
console.log("if (a)")
return ContentService.createTextOutput("a is : " + a).setMimeType(ContentService.MimeType.TEXT)
}
else {
console.log("else (a)")
return ContentService.createTextOutput("ID not found").setMimeType(ContentService.MimeType.TEXT);
}
}
Solution:
If the conditonal statement shall only evaluate to true for not empty a, rewrite the if condition as
if (a.length > 0)

Copy last updated row from Sheet1 to Sheet2

data.forEach(function(f) {
if (~f.indexOf(searchtext)) {
ar.push(f);
}
});
return ar;
}
function isUpdated(data) {
var arrayLength = data.length;
var updated = false;
var oldData = search(data[1]);
for (var i = 1; i < arrayLength; i++) {
if(oldData[0][i] != data[i]){
updated = true;
break;
}
}
return updated;
}
function update(data){
data = JSON.parse(data)
const tag = data[1].toString().trim().toUpperCase()
const ws = SpreadsheetApp.openById(SS_ID).getSheetByName(SN_DATA)
const values = ws.getDataRange().getValues()
const index = values.findIndex(v => v[1].toString().trim().toUpperCase() === tag)
if (index === -1) return false
ws.getRange(index + 1, 1, 1, data.length).setValues([data])
return true
}
Hello expert I'm able to write a code to search and update using Web App. google Sheet Sheet1 can anyone help ,e how to copy my last edited/updated rows to sheet2? I want to add row function every time the sheet1 has been updated the updated column will logs to sheet2? code has been provide above

function is not getting the value from parent function - google apps script

i have table, the headers are the project number, the left column is the workers name, i am adding code to start and stop working for each workers in the project.
so, when user edit the suitable rectangle it will add new (in or Out) record about that worker and the project.
I have problem with the function called area() , it is not getting the range value from the function onEdit(e).
it is working only when i put the value inside the area() function itself., for example when i put
var vrange = "S10"
or i run it with the fixed value such area("S10")
it is not getting the range variable value when the other functions are getting it correctly
but the range value is dynamic as per the user editing cell.
the problem is happening in this row for only area(range) function, the other functions such getProject(range) is getting the range value correctly.
addRecord(getProject(range),getWorker(range),new Date(),"un",area(range))
hope somebody can know what is my mistake.
here is the code
function onEdit(e) {
var range = e.range
Logger.log("e.range" + range)
var Currentsheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName()
if (Currentsheet == TimeInterface()) {
addRecord(getProject(range), getWorker(range), new Date(), "un", area(range))
}
}
function area(vrange) {
// var frange = "S10"
//var frange = vrange
Logger.log("vrange is " + vrange)
var currentrow = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(vrange).getRow()
var currentcol = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(vrange).getColumn()
var outColumn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B1").getValue()
var recordingFirstColumn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B2").getValue()
var recordingLastColumn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B3").getValue()
var recordingFirstRow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B4").getValue()
var recordingLastRow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(RecordSettingsSheet()).getRange("B5").getValue()
var answer = ""
if (currentcol == outColumn && currentrow >= recordingFirstRow && currentrow <= recordingLastRow) {
answer = "out"
return answer
} else if (currentrow >= recordingFirstColumn && currentrow <= recordingLastColumn && currentrow >= recordingFirstRow && currentrow <= recordingLastRow) {
answer = "in"
return answer
} else {
answer = "Error"
return answer
}
}
function TimeInterface() {
var TimeInterfacev = "Interface"
return TimeInterfacev
}
function RecordSettingsSheet() {
var recordSettingsSheet = "Record Settings"
return recordSettingsSheet
}
function getProject(range) {
var ProjectID = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(1, range.getColumn()).getValues()
Logger.log("getProject range is " + range)
return ProjectID
}
function getWorker(range) {
var WorkerID = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(range.getRow(), 1).getValues()
return WorkerID
}
function setValue(cellname, Value) {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheetname()).getRange(cellname).setValue(Value);
}
function getValue(cellname) {
return SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheetname()).getRange(cellname).getValue();
}
function Sheetname() {
var Sheet = "TimeRecords"
return Sheet
}
function newRowNumber() {
var row = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(Sheetname()).getLastRow() + 1;
return row
}
function addRecord(a, b, c, d, e) {
var row = newRowNumber();
var aa = getValue("A" + (row - 1))
var bb = getValue("B" + (row - 1))
var cc = getValue("C" + (row - 1))
var dd = getValue("D" + (row - 1))
if (
a == aa && b == bb && d == dd
) {
} else {
setValue("A" + row, a);
setValue("B" + row, b);
setValue("C" + row, c);
setValue("D" + row, d);
setValue("E" + row, e);
}
}
here is the error detailes
{
insertId: "ofc7bcfvzisc2"
jsonPayload: {
context: {
reportLocation: {
filePath: "Code"
functionName: "area"
lineNumber: 22
}
}
message: "Range not found
at area(Code:22)
at onEdit(Code:12)
"
serviceContext: {
service: "AKfycbyZxuwklLCadxRO93yHKQH0HO34bM9fReTelwt2I39_"
}
}
labels: {
script.googleapis.com/process_id: "EAEA1GOx_xjeztWsGim9PR3gYwydfe6FUt9_U2QNlaA6mIDJNtAm21jdbcWIBqG3z1Hg9CPQuaS_Hf5tJYJh8XqtqDQvgwfh1AX6DQ1M5Jc5tfPEOrgRj7eM4-ueK9m5FidEZBs31l3e-MsG9aHj4JLFEcsntzvvPMZB20Q"
script.googleapis.com/project_key: "MvVaniAhXvNj_E5qpMKyh9JCBc1Z0ol9u"
script.googleapis.com/user_key: "AJw2Yz+oU8zemc1ioio5IJNfc43E3QhuOpzprTTJO/RULzh/3VQSMjron9r4W4J+nuGW9Hj2YCF1"
}
logName: "projects/project-id-5448248979683187508/logs/script.googleapis.com%2Fconsole_logs"
receiveTimestamp: "2018-08-11T15:29:53.172475577Z"
resource: {
labels: {
function_name: "onEdit"
invocation_type: "event"
project_id: "project-id-5448248979683187508"
}
type: "app_script_function"
}
severity: "ERROR"
timestamp: "2018-08-11T15:29:52.164Z"
}
I solved the problem with using
getRange(range.getRow(), range.getColumn()).getRow()
instead of
getRange(range).getRow();