Google Script version of VLookup (More Efficient Method?)

后端 未结 2 933
自闭症患者
自闭症患者 2021-01-29 06:11

I\'m trying to put together a function that will allow me to pull a column\'s info from one sheet to another based on a key column. This would work similar to an index match or

相关标签:
2条回答
  • 2021-01-29 06:34

    Unfortunately I am afraid there is no built-in function for this in Apps Script.

    However, I have tried your custom function with 100 values and it took <3 seconds to run. I also ran it with 1000 values and my running time was around 40 seconds. It is not ideal but it works consistently. Here is the code I have used:

        var sheet = SpreadsheetApp.getActiveSpreadsheet();
        var importFromSht = SpreadsheetApp.getActive().getSheetByName('Sheet1'); //sheet we are grabbing the values of
        var importToSht = SpreadsheetApp.getActive().getSheetByName('Sheet2');  //sheet we are pasting our values
        var importFromCompCol = 2; // Column (number) that has values to match on.
        var importToCompCol = 2; // Column (number) that has values to match on.
        var importFromCol = 1; // Column (number) that contains value that needs to be copied.
        var importToCol = 1; // Column (number) to copy value to.
        
        function customVlookup (){
          var lastImportFromRN = importFromSht.getLastRow();
          var lastImportToRN = importToSht.getLastRow();
          var importFromCompArr =  importFromSht.getRange(1, importFromCompCol, lastImportFromRN, 1).getValues();
          var importToCompArr =  importToSht.getRange(1, importToCompCol, lastImportToRN, 1).getValues();
          var importFromArr =  importFromSht.getRange(1, importFromCol, lastImportFromRN, 1).getValues();
          var importToArr = [];
        
          for (var i in importToCompArr) {
            for (var j in importFromCompArr){
              if (importToCompArr[i].toString() == importFromCompArr[j].toString()) { 
                importToArr.push(importFromArr[j]);
              }
            }
          }
          //Paste to column
          importToSht.getRange(1,importToCol,importToArr.length,1).setValues(importToArr);
        }

    A different and more efficient approach is to use IMPORTRANGE(URL of first sheet) to an intermediate sheet swapping the columns of the first sheet to then do a VLOOKUP on your second sheet. This would be way more efficient than doing it in Apps Script as it does not run in the memory and you avoid the issue of exceeding the execution time of a custom function.

    In case you want to keep it as an Apps Script custom function Here are some suggestions on the documentation on how to improve your function’s efficiency.

    0 讨论(0)
  • 2021-01-29 06:51

    Issue:

    • Slow script:

      • O(n2): For every element in array 1, the array 2 is iterated from top to bottom. Even after finding a match in array 2, the loop is not broken(break), but the inner loop is completed until the end of the array2 unnecessarily.

      • getValues() is requested twice for two columns of the same sheet. Contact with spreadsheet is costly. So, limiting it is necessary.

    Solution:

    One possible solution to achieve O(n):

    • Create a new object using array 1 with key as 'the value to look for'. Then it's possible to directly access the value in this object each time for each value in array 2.

    Sample script:

    const ss = SpreadsheetApp.getActive();
    /**
     * @param {GoogleAppsScript.Spreadsheet.Sheet} fromSht -Sheet to import from
     * @param {GoogleAppsScript.Spreadsheet.Sheet} toSht -Sheet to import to
     * @param {Number} fromCompCol -Column number of fromSht to compare
     * @param {Number} toCompCol -Column number of toSht to compare
     * @param {Number} fromCol -Column number of fromSht to get result
     * @param {Number} toCol -Column number of toSht to get result
     */
    function vlookup_2(
      fromSht = ss.getSheetByName('Sheet1'),
      toSht = ss.getSheetByName('Sheet2'),
      fromCompCol = 1,
      toCompCol = 1,
      fromCol = 2,
      toCol = 2
    ) {
      const toShtLr = toSht.getLastRow();
      const toCompArr = toSht.getRange(2, toCompCol, toShtLr - 1, 1).getValues();
      const fromArr = fromSht.getDataRange().getValues();
      fromCompCol--;
      fromCol--;
    
      /*Create a hash object of fromSheet*/
      const obj1 = fromArr.reduce((obj, row) => {
        let el = row[fromCompCol];
        el in obj ? null : (obj[el] = row[fromCol]);
        return obj;
      }, {});
    
      //Paste to column
      toSht
        .getRange(2, toCol, toShtLr - 1, 1)
        .setValues(toCompArr.map(row => (row[0] in obj1 ? [obj1[row[0]]] : [null])));
    }
    
    

    Performance:

    • ~5s for 10000 rows in sheet1 and 10000 rows in sheet 2

    References:

    • Time complexity
    • Hash map
    0 讨论(0)
提交回复
热议问题