1

I am trying to find out what the fastest way is to get some data from a row where a value matches my query, I am currently using this code:

var spr = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('CustomerList'); 
  var klantNummer = array1[0].toString();
  var emailgebruiker = Session.getActiveUser().getEmail();      
  var data = spr.getDataRange().getValues(); 
  for(n=0;n<data.length;++n){ 
    if(data[n][0].toString() == klantNummer){ //.match(klantNummer)
      data[n][21] = emailgebruiker

   var KlantNR = data[n][0];
   var Email = data[n][3];
   var Stad = data[n][12];          
   var Taalschool = firstToUpperCase( data[n][13].toString() );    
   var Cursus = firstToUpperCase( data[n][14].toString() );      
   var Weken = data[n][15];
   var Accommodatie1 = data[n][17];
   var TypeAccommodatie = data[n][18];
   var TypeKamer = data[n][19];
   var VertrekDatum1 = data[n][20];

    };
  }
  spr.getRange(1,1,data.length,data[0].length).setValues(data); 

It works fine when I use it in a small sheet but when the sheet gets bigger it starts to become slow, is there a better way to do this?

BjörnBogers
  • 161
  • 3
  • 16

2 Answers2

3

There is a quick solution that would solve for some cases. You can break the loop after you found a match. This however would not help if your data is at the bottom of your list.

if(data[n][0].toString() == klantNummer){ //.match(klantNummer)
   data[n][21] = emailgebruiker;
   var KlantNR = data[n][0];
   var Email = data[n][3];
   .
   . 
   .  
   var VertrekDatum1 = data[n][20];
   break;
 }

A second way of speeding up a search is to order your data by your search key, klantNummer in your case, and performing a binary search. Without knowing your dataset though this may not be faster as you first need to sort the set before you can search. You can solve this by having a duplicate sheet of your customers that is pre-sorted by customer number. That would be the sheet you use to do you search.

Here is a binary search in javascript. This is from https://github.com/Wolfy87/binary-search. I modified it to work with multidimensional arrays as the way getValues() return them. The last argument in search index in which column the customer number is in. This code below was returning the row number in about .139 seconds in a dataset off 2000 records.

  function myFunction() {
  var customerId = 11359;
  var ss = SpreadsheetApp.getActiveSheet();
  var range = ss.getDataRange();
  var customers = range.getValues();  
  var index = binarySearch(customers, customerId,0);

  Logger.log(index+1); // This is the Row in the spreadsheet.


}


/**
 * Performs a binary search on the provided sorted list and returns the index of the item if found. If it can't be found it'll return -1.
 *
 * @param {*[][]} list Items to search through.
 * @param {*} item The item to look for.
 * @return {Number} The index of the item if found, -1 if not.
 */
function binarySearch(list, item,column) {
    var min = 0;
    var max = list.length - 1;
    var guess;
    var column = column || 0

    while (min <= max) {
        guess = Math.floor((min + max) / 2);

        if (list[guess][column] === item) {
            return guess;
        }
        else {
            if (list[guess][column] < item) {
                min = guess + 1;
            }
            else {
                max = guess - 1;
            }
        }
    }

    return -1;
}
Spencer Easton
  • 5,642
  • 1
  • 16
  • 25
2

I've found that JSON object saved as a text file inside Drive works really fast, a 6mb text file is read then parsed in under 200 milliseconds. With this, you can save all lines of the klantNummer to the Object and call it, get the lines and get all values, under 1 sec, as such:

Save, update and get klantNummer examples:

function saveFile(){
  var klanNumbers = {'ben@gmail.com ': 3, 'dora@gmail.com' : 12, 'ksd@gmail.com' : 33};
  return DriveApp.createFile('klanNumbers', JSON.stringify(klanNumbers));
}

function updateFile( newKlanNumb, klanLine ){
  var klanNumbFile = DriveApp.getFilesByName('klanNumbers').next(),
      file = JSON.parse(file.getAs("application/none").getDataAsString());

  if( file[ newKlanNumb ] )
    file[ newKlanNumb ] = file[ newKlanNumb ] + ',' + klanLine;
  else
    file[ newKlanNumb ] = klanLine;

  klanNumbFile.setTrashed(true);

  return DriveApp.createFile('klanNumbers', JSON.stringify( newKlanNumb ));
}

function getKlanNumbers( klanNumb ){
  var file = DriveApp.getFilesByName('Transactions').next(),
      info = file.getAs("application/none").getDataAsString(),
      klanNumbers = JSON.parse(info);

  return klanNumbers[klanNumb];
}
Kriggs
  • 3,731
  • 1
  • 15
  • 23
  • That's impressive! I'm going to try this out. – BjörnBogers Mar 30 '15 at 13:40
  • `[15-03-30 15:48:05:215 CEST] DriveApp.getFilesByName([klanNumbers]) [0 seconds] [15-03-30 15:48:05:348 CEST] (class).next() [0.132 seconds] [15-03-30 15:48:05:349 CEST] File.getAs([application/none]) [0 seconds] [15-03-30 15:48:05:478 CEST] Blob.getDataAsString() [0.129 seconds] [15-03-30 15:48:05:509 CEST] Browser.msgBox([undefined]) [0 seconds]` Is what I'm getting back from the log – BjörnBogers Mar 30 '15 at 13:49
  • My "Klantnummers" start @ 100001, that is what goes wrong here. 100001 = KlanNumbers[1]. I know I can replace the 10000 by "" so that I have 1 but when I'm at 100010 that goes wrong.. How can I fix that? The execution time is awesome! – BjörnBogers Mar 30 '15 at 14:03
  • KlanNumbers isn't an Array, but an object, so actuially you have `KlanNumbers[ '100001' ] = 1;`. This means that when you provide the KlanNumber 100001, the object will return 1, wich is the line number. – Kriggs Mar 30 '15 at 14:05
  • `[["Klant nr.","Voornaam","Achternaam","E-mail","Telefoonnummer","Dob","Status","Totaal bedrag","Verstuurd d.d.","Bedrag ontvangen","UitersteDatum","Taal","Bestemming","Taalschool","Cursus","WK","Coupon","ACC","Type acco","Type kamer","Vertrek datum","BH","Opvolgen","Comment","Aanmaak d.d.","Straat","Postcode","Stad","Land"],["100001"," xxx"," xxx","xxx@xxxx.xx",xxx,"","Cnpt 2","","","",""," engels"," cambridge"," ec-cambridge"," General English",6,"","","","","","","","","2015-03-08T23:00:00.000Z","","","",""]]` This is the content of the file: "klanNumbers". Return klanNumbers[1] works here – BjörnBogers Mar 30 '15 at 14:10
  • 1
    That works as an array, which requires a loop to find the desired value, the notation of an Array is `[value1, value2]`, in which every value takes the next avaible numerical value as index. In an Object, the notation is `{index :value, index2, value2}`, in which you specify the values key (or index), this way you don't have to search trough the array, you just pointo where you want, that's why it's faster to use an Object, you'll have to refactor your code so it only save the KlanNumber as the key and the line number as the value of the key. – Kriggs Mar 30 '15 at 14:16
  • Ah okay, can I do that like this: var klanNumbers = {'ExampleVal1','ExampleVal2','ExampleVal3': 100001} ? – BjörnBogers Mar 30 '15 at 14:20
  • 1
    Sorry, the correct notation would be `{index :value, index2: value2}`, the second comma was misclicked. You can read more about objects in http://www.w3schools.com/js/js_objects.asp, I really recommend using Object, my codes got alot cleaner and faster after learning all the ways of using them. – Kriggs Mar 30 '15 at 14:47
  • `file.getAs("application/none").getDataAsString()` doesn't seem to work anymore.. – BjörnBogers May 27 '15 at 07:06
  • If anyone needs this code to work, use: **file.getAs("text/plain").getDataAsString()** and it will work again! – BjörnBogers May 27 '15 at 07:28
  • @BjörnBogers I'm using `file.getBlob().getDataAsString()`. – Kriggs May 27 '15 at 10:52
  • is that safer to use? Because I want to make sure this doesn't happen again! :) – BjörnBogers May 28 '15 at 07:54