首页 > 解决方案 > 仅附加唯一行

问题描述

我在构建一个脚本时遇到了困难,该脚本只会附加当前不存在于工作表上的行。

我有一个主表,我正在向其中导入csvData. 目前,我有 1 个用于导入 csv 数据的脚本和另一个用于在导入后从可能的重复项中清除工作表的脚本。尽管这可行,但重复删除脚本使用.clearContent并被迫在返回唯一行列表之前完全清除工作表。由于电子表格是在外部使用的(通过 Appsheet),因此如果任何用户在执行脚本时尝试向工作表添加任何内容,则会产生未记录/损坏数据的风险。

因此,我正在尝试构建另一个脚本,该脚本将导入csvData一个空数组,然后将其与从主表中提取的数组进行比较。通过比较,该脚本只会附加主表上尚不存在的行。

不幸的是,我无法找到没有清除工作表内容的重复删除示例。我有另一个想法来计算重复并只选择没有重复的那些,但我也无法制作一个工作脚本/找到一个例子。

下面是我的原始脚本,其中包括导入和重复删除使用.clearContent

function importEMS() {
  var fSource=DriveApp.getFolderById('folder id removed'); 
  var fi=fSource.getFilesByName('EMS.csv'); 
  var ss=SpreadsheetApp.openById('sheet id removed');

  //CONVERT CSV FILE TO A TABLE
  if (fi.hasNext()) { 
    var file=fi.next();
    var csv=file.getBlob().getDataAsString();
    var csvData=CSVToArray(csv);
    var timestamp = new Date();
    var tsh=ss.getSheetByName('Main');
    for (var i=1;i<csvData.length-1;i++) {
      csvData[i][8] = timestamp;
    }

   //APPEND NEW ROWS

    for (var i=1;i<csvData.length;i++) {
      tsh.appendRow(csvData[i]);
    }
  }

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Main");
  var data = sheet.getDataRange().getValues();
  var newData = new Array();
  for(i in data){
    var row = data[i];
    var duplicate = false;
    for(j in newData){

      if(row.slice(0,7).join().toLowerCase() == newData[j].slice(0,7).join().toLowerCase()){
        duplicate = true;
      }
    }
    if(!duplicate){
      newData.push(row);
    }
  }
  // Clear the existing info and update with newData.
  sheet.clearContents();
  sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}

有谁知道如何仅将新行而不是整个附加csvData到主表?

我在这里先向您的帮助表示感谢!

UPD:添加了数据示例的链接。真正的主表包含 20 列,但我从示例中删除了它们,列顺序相同。CSV 导入表是我导入的数据类型。在该表上,我用绿色突出显示了我试图附加到主表的行。

UPD2:由 Oleg Valter 函数提供的解决方案,但前提是我替换.getValues().getDisplayValues(),它将所有当前类型的数据转换为字符串,这也是导入 .csv 数据的格式。

标签: google-apps-scriptgoogle-sheets

解决方案


解决方案

  1. 将 CSV 值作为数组数组获取(假设是CSVToArray()这样)。
  2. 通过 获取当前值作为数组数组getDataRange().getValues()
  3. 过滤掉这样的重复值(假设每对行具有相同的长度- 否则算法会变得更加复杂,但从您的代码来看,您只检查前 8 个值):

//source = [[...], ... , [...]];
//target = [[...], ... , [...]];

/**
 * Checks if at least one element
 * is positioned differently
 * @param {*[]} a
 * @param {*[][]} b
 * @returns {Boolean}
 */
var unique = function (a,b) {
  return b.every(function(bRow){
    return a.some(function(A,aIdx){
      return A !== bRow[aIdx];
    });
  });
};

/**
 * Leaves only values not present in source
 * @param {*[][]} source
 * @param {*[][]} target
 * @returns {*[][]}
 */
var filterUnique = function (source,target) {
  return target.filter(function (row) {
    return unique(row,source);
  });
};

var S1 = [[1,2,3],['A','D','C'],[5],[7]];
var T1 = [[1,2,3],['A','B','C'],[6]];

var S2 = [[1.00,'Vehicle 1',1.00,'Ready','12-5-2020',	1, 'Event A',	'Location A']];
var T2 = [S2[0],[2.00,'V2',1.00,'Ready','12-5-2020', 1, 'Event A', 'Location A']];

var check1 = filterUnique(S1,T1);
var check2 = filterUnique(S2,T2);

console.log(check1,check2);

笔记

  1. 您需要在仅保留唯一行之后将检查与时间戳和仅时间戳交换,否则存在附加该行的风险,因为它的时间戳是唯一的差异。

参考

  1. every() MDN 上的方法参考
  2. some() MDN 上的方法参考
  3. filter() MDN 上的方法参考

推荐阅读