0

I modified the code given by @Alan Wells in Find position of a cell containing a specific string but I don't know why it still inserts the matched string into the next empty cell. It supposed to only insert IF no match found in the entire data range declared in values array. Please help to modify my code below. Thanks.

function findValueInRange () {
  var ss=SpreadsheetApp.openById('THE_SHEET_ID').getSheetByName('staff'),
      theLastColumn = ss.getLastColumn(),
      theLastRow = ss.getLastRow(),
      lastrow=theLastRow,
      name='Dahlia';
  var values = ss.getRange(1,1,theLastRow,theLastColumn).getValues();

  var i=0,rownum=0,thisRow,colvalue,whatToFind = name,j=0;

  for(i=0;i<values.length;i++) {
    thisRow = values[i];
    for (j=1;j<thisRow.length;j++) {
      colvalue = thisRow[j];
      switch(colvalue){
        case whatToFind :Logger.log("The string is in row: "+(i+1)+" and column: "+(j+1));break;
        case '' : Logger.log("just blank");break;
        default : // when no found match
           var lrow=NextCellRng(theLastColumn);//a custom fn to get the last non-blank row number of the last column only
           ss.getRange(lrow,theLastColumn-1).setValue(name); //insert name in the next cell range
           ss.getRange(lrow,theLastColumn).setValue('attended'); // insert status next to the name
           break;
      }
    }
  }
}

Here is the spreadsheet that I need to refer to lookup for the string:-
lookupsheet
This spreadsheet stores 2 columns of data which are the name and status 'attended'. I want to utilise the spreadsheet spaces to it's max limit of rows and columns, so once it reaches the max row of first column, it supposed to continue in the next column and so forth. So, the data and search range would grow with the new inserted data when not found. It should be able to grow until the max row and max col that a sheet could handle. In this question, we assume that the max row (5 rows) is reached and that's why the next column is filled and so forth. Whenever a name is entered in a googleform, my custom function should be able to do a validation first:-

  1. getValue from the response sheet (in this question, 'Dahlia' is for sampling test)
  2. search for the name in the lookup sheet. If exist, return the col & row number & out of loop. But if not, insert the name in the next blank cell and out of loop

But what i noticed is, regardless the name exist or not, the name is still will be inserted. And worse, I realized is the insert process is an infinite loop for that same last column. I had to terminate it at the Project trigger dashboard when I advertently clicked "dismiss" on the script earlier.
When name 'Dahlia' is not found the updated sheet should look like below:-
expectedoutput

dell
  • 171
  • 13
  • In order to correctly understand about `It supposed to only insert IF no match found in the entire data range declared in values array.`, can you provide the sample input and output you expect as the image? By the way, I think that in your script, errors occur at `Logger.log("The string is in row: "+(i+1)+" and column: "(j+1))` and `Logger.log("MaxRows : "+theLastRow,"currentcol : "+currentcol);`. But you say `it still inserts the matched string into the next empty cell`. So I cannot understand about your script. How about this? – Tanaike Oct 13 '20 at 06:25
  • Sorry for the many errors, Mr. @Tanaike . I often made changes or deleted some code lines or commands after pasting my code in here just to either simplify them or removing those which I no longer used or just to create a different sampling scenario. – dell Oct 13 '20 at 09:05
  • However, I have edited the code and put more explanation and screenshots for your kind perusal. I hope they are sufficient for you. Thank you. – dell Oct 13 '20 at 09:06
  • Thank you for replying and adding more information. In that case, how do you want to think of the cells after the row 6 and the column "G"? Unfortunately, I cannot understand about the logic that the values are put to the cells "E4" and "E5" as the next empty cells. I apologize for my poor understanding. – Tanaike Oct 13 '20 at 09:15
  • Thank you for the swift response, Mr. @Tanaike . well, if possible, when it reaches the max row and max col of the sheet, an email to alert the sheet owner to archive the sheet data elsewhere and clear all the content of 'staff' sheet for new data. – dell Oct 13 '20 at 09:29
  • When the old sheet is archived, even there is the same name appear again, it would be accepted as new data. Sorry, for now, I couldn't think of any other ways to let my code continue to search other worksheet(s) if not found in 'staff' sheet. – dell Oct 13 '20 at 09:30
  • Even if there is any, it would eventually bog down the runtime as it has to check millions of entries. But, any best idea to comprehend this efficiently is much appreciated. – dell Oct 13 '20 at 09:31
  • `about the logic that the values are put to the cells "E4" and "E5" as the next empty cells`, I'm sorry if I cannot make it clearer enough for your understanding but basically my objective is, if not found the name in the sheet, it should be updated into the sheet as the new data. So that, the next time, the same user enters the same name, it checks up and found a match and return rownum and rowcol. I need this info for other processes which I feel not necessary to list all the codes in here. – dell Oct 13 '20 at 09:40
  • Thank you for replying. But I have to apologize for my poor English skill. Unfortunately, I couldn't still understand about the logic that the values are put to the cells "E4" and "E5" as the next empty cells. – Tanaike Oct 13 '20 at 22:55

2 Answers2

1

The break expression within the switch statement only breaks out of the switch, not of the for loops

Thus, even after successfuly finding a name (in one cell), the function will keep looping through the other cells and insert the name in all cells where it is not contained.

  • What you need to do is to introduce a global boolean that reflects the status of finding the name
  • Once the name is found - set the boolean to true
  • Check at the end of each inner loop iteration either the boolean is true
  • Once it's true (the name has been found) - break out of the for loops
  • After the exiting the for loops without finding the value - go ahead with inserting the value
  • The code block setting the value will not be run, if the value has been found in the sheet, and thus the function has been exited before finishing the full iteration

Sample:

  var found = false;
  for(i=0;i<values.length;i++) {
    thisRow = values[i];
    for (j=0;j<thisRow.length;j++) {
      colvalue = thisRow[j];
      Logger.log(colvalue);
      switch(colvalue){
        case whatToFind :
          Logger.log("The string is in row: "+(i+1)+" and column: "+(j+1));
          found = true;
          break;
        case '' : 
          Logger.log("just blank");
          break;
        default : // when no found match
          Logger.log("default");
          break;
      }
      if(found==true){
        break;
      }
    }
  }
  var lrow=NextCellRng(theLastColumn);//a custom fn to get the last non-blank row number of the last column only
  ss.getRange(lrow,theLastColumn-1).setValue(name); //insert name in the next cell range
  ss.getRange(lrow,theLastColumn).setValue('attended'); // insert status next to the name
ziganotschka
  • 25,866
  • 2
  • 16
  • 33
  • Thanks for the modified code, @ziganotschka . It does stop the loop. However, it still insert the same data when I run it again. Seems like it doesn't recognize the string as exist or it just doesn't exit the function once found. :( – dell Oct 14 '20 at 17:21
  • I've tried multiple times to change the position of the code line that inserts the name if not found, thinking that the inserting may happen regardless found or not maybe because that code line is set to run after finish looping. I've also tried the command `return` to make it exit the function once found but still doesn't work. :( – dell Oct 14 '20 at 17:24
  • Maybe it's a problem with case sensitivity or hidden spaces? I can have a look at your sheet. – ziganotschka Oct 14 '20 at 17:28
  • 1
    Thanks for your swift response, @ziganotschka . I tweaked a little bit on your code and finally got what I needed. Thanks again for your time and effort. – dell Oct 14 '20 at 18:08
0

Thanks a lot to @ziganotschka because his answer has somehow given me some realization on how to really exit the function when search string has been found and insert as new data if not found. We need to use the return command, just that must be placed at the right line of code. So, here's my modified code and answer to what I needed :-

function findValueInRange () {
  var ss=SpreadsheetApp.openById('THIS_SHEET_ID').getSheetByName('staff'),
      theLastColumn = ss.getLastColumn(),
      theLastRow = ss.getLastRow(),
      lastrow=theLastRow,
      name='Dahlia';
  var values = ss.getRange(1,1,theLastRow,theLastColumn).getValues();
  var i=0,rownum=0,thisRow,colvalue,whatToFind = name,j=0;
  
  for(i=0;i<values.length;i++) {
    thisRow = values[i];
    for (j=0;j<thisRow.length;j++) {
    //for (j=1;j<=lastrow;j++) {
      colvalue = thisRow[j];
      switch(colvalue){
        case whatToFind :Logger.log("The string is in row: "+(i+1)+" and column: "+(j+1));return;
        case '' : Logger.log("just blank");break;
        default : Logger.log("default"); break;// when no found match
      }
    }
  }
  var lrow=NextCellRng(theLastColumn);//a custom fn to get the last non-blank row number of the last column only
  ss.getRange(lrow,theLastColumn-1).setValue(name); //insert name in the next cell range
  ss.getRange(lrow,theLastColumn).setValue('attended'); // insert status next to the name 
}
dell
  • 171
  • 13