【问题标题】:Append unique rows only仅附加唯一行
【发布时间】:2020-02-03 15:57:44
【问题描述】:

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

我有一个主表,我正在向其中导入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:向数据样本添加了link。真正的主表包含 20 列,但我从示例中删除了它们,列顺序相同。 CSV 导入表是我导入的数据类型。在该表上,我用绿色突出显示了我试图附加到主表的行。

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

【问题讨论】:

  • 你能分享一份你的数据文件和你的主表的样本副本吗?
  • 有多个EMS.csv 文件吗?为什么除了第一行和最后一行之外的所有行都附加时间戳?
  • 您已经描述了这个问题,在我看来,您可能需要重新考虑这个问题。 CSVToArray 返回一个二维数组,对吧? getValues() 也是如此。如果您使用了filter() Array 方法,您可以只留下不等于getValues() 返回的任何内容,然后追加结果。请问您是否需要toLowerCase()比较值?
  • @OlegValter,我使用.toLowerCase() 来避免重复,因为有些销售是字母数字或纯文本。我了解您的解决方案的逻辑,但我无法真正在代码方面对其进行转换。您是否有一个如何使用 filter() 的示例?
  • @OlegValter 谢谢!明天上班去看看,看看有没有效果!

标签: google-apps-script google-sheets


【解决方案1】:

解决方案

  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() method ref 在 MDN 上
  2. some() method ref 在 MDN 上
  3. filter() method ref 在 MDN 上

【讨论】:

  • 感谢您的回答!但是,我无法让它正常工作。我使用data 作为源,csvData 作为目标,newData 作为目标。当我运行filterUnique 函数时,它会将整个csvData 范围返回到newData。您知道为什么会发生这种情况吗?
  • 没关系,那是我的错误!我在 gSheet 上启用了数据格式,所以它没有正确读取日期。我将它全部设置为纯文本,现在它可以工作了!感谢您的帮助!
  • 2.这是对上一条评论的一种扩展:如果可以,将数据从CSV更改为标准格式(如果您确切知道它始终在哪一列,则可以这样做在按值索引进行比较时),然后添加上述保护,如bRow[aIdx] instanceof Date ? bRow[aIdx].valueOf() !== new Date(A).valueOf() : A !== bRow[aIdx]
  • 嗨奥列格!很抱歉一直打扰你,但我担心你的建议没有奏效。我确实成功地将日期转换为相同的格式,但它仍然无法正常工作。然后我想,csvData 中的数字实际上被视为字符串,而在工作表上它们被识别为数字。然后我将所有带数字的字符串转换为数字,但脚本仍然看不到现有行和新行之间的任何区别。如果我检查记录器,csvDatadata 中的行看起来完全一样。你知道这可能是什么问题吗?
  • 更新了我的主帖,添加了我当前的代码和一个小解释
猜你喜欢
  • 2016-11-04
  • 2012-07-24
  • 1970-01-01
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
  • 2015-01-03
  • 2022-01-02
  • 2014-10-31
相关资源
最近更新 更多