VLookup的Google脚本版本(更有效的方法?)

时间:2020-02-17 03:59:29

标签: google-apps-script google-sheets google-sheets-formula

我正在尝试组合一个函数,该函数将允许我根据关键列将一列的信息从一张纸拉到另一张纸。这将类似于excel / google中的索引匹配或vlookup。

样本数据:

enter image description here

我尝试过的事情:

function vlookup(importFromSht, importToSht, importFromCompCol, importToCompCol,importFromCol, importToCol){
  var lastImportFromRN = importFromSht.getLastRow();
  var lastImportToRN = importToSht.getLastRow();
  var importFromCompArr =  importFromSht.getRange(2, importFromCompCol, lastImportFromRN, 1).getValues();
  var importToCompArr =  importToSht.getRange(2, importToCompCol, lastImportToRN, 1).getValues();
  var importFromArr =  importFromSht.getRange(2, 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(2,importToCol,importToArr.length,1).setValues(importToArr);
}

已定义参数

  • importFromSht-我们正在从中获取值的表。
  • importToSht-工作表值将被复制到。
  • importFromCompCol-具有匹配值的列(数字)。
  • importToCompCol-具有匹配值的列(数字)。
  • importFromCol-包含需要复制的值的列(数字)。
  • importToCol-要复制值的列(数字)。

要求:

  • 至少有超过6K的行,并且可能更多。执行速度很重要。我的方法是正确的方法还是有更有效的方法?我的脚本使执行时间增加了大约30秒。
  • 我想看到它在本项目和其他项目的其他领域中使用时,将其称为函数。

2 个答案:

答案 0 :(得分:3)

问题:

  • 慢速脚本:

    • O(n 2 ):对于数组1中的每个元素,数组2从顶部到底部进行迭代。即使在数组2中找到匹配项后,循环也不会中断(break),但是内部循环会完成,直到不必要地在array2结束为止。

    • 对于同一张纸的两列,两次请求
    • getValues()。与电子表格联系非常昂贵。因此,限制它是必要的。

解决方案:

一种实现O(n)的可能解决方案:

  • 使用键1作为“要查找的值”的数组1创建一个新对象。这样就有可能每次都为数组2中的每个值直接访问此对象中的值。

示例脚本:

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])));
}

性能:

  • 在工作表1中的10000行和工作表2中的10000行的〜5s

参考文献:

答案 1 :(得分:0)

很遗憾,我担心Apps脚本中没有内置函数。

但是,我尝试了100个值的自定义函数,并且花费了不到3秒的时间。我还用1000个值运行它,我的运行时间约为40秒。这不是理想的方法,但是可以始终如一地工作。这是我使用的代码:

    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);
    }

另一种更有效的方法是使用IMPORTRANGE(第一张纸的URL)到中间纸,交换第一张纸的列,然后在第二张纸上做VLOOKUP。这将比在Apps脚本中执行效率更高,因为它不会在内存中运行,并且可以避免超出自定义函数执行时间的问题。

如果您想将其保留为Apps Script自定义功能Here,请参考有关如何提高功能效率的文档的一些建议。