Google App脚本将多个工作表合并到一个母版中

时间:2018-06-22 11:04:33

标签: javascript jquery google-apps-script

我有一个很小的销售团队(有5个人,但正在增长),可以将销售记录到工作簿中的多个工作表中,我想运行一个脚本以将所有这些工作表(如果可能的话)与一个脚本合并。

因此阶段将是每天

1)从每日主数据中删除所有记录(这是为了解释各个销售单中先前数据的任何更改) 2)循环浏览所有工作表并添加数据

共有17列,它们的顺序相同但包含不同的数据。

我尝试使用/编辑/转换以下脚本无济于事,我不确定在indexof('Project')猜测它是布尔值语句后,> -1是什么意思?

我确实尝试将Project添加到我的每个团队工作表中,结果是在Master工作表中只返回了一个工作表。

代码尝试:

function merge() {
    var v, arr,
        ss = SpreadsheetApp.getActive();
    ss.getSheets().filter(function(s) {
        return s.getName()
            .indexOf('Project') > -1
    }).forEach(function(s, i) {
        v = s.getDataRange()
            .getValues()
            .filter(function(r) {
                return r.toString()
                    .length > 0
            })
        arr = (i == 0) ? v : arr.concat(v);
    })
    ss.getSheetByName('Master')
        .getRange(1, 1, arr.length, arr[0].length)
        .setValues(arr);
}

任何帮助将不胜感激。

谢谢!

5 个答案:

答案 0 :(得分:1)

尝试使用SheetGo插件,只需单击几下即可完成工作

看看这个: https://blog.sheetgo.com/how-to-solve-with-sheetgo/merge-data-multiple-google-sheets/

答案 1 :(得分:1)

这些是主要功能。

dailysheets.gs:

IFormFile

这些文件只是我经常使用的一些支持文件,在本项目中我使用了其中的一些内容。

triggers.gs:

public class XmlFileAddViewModel
{
    [Required(ErrorMessage = "File Content should be given"),Display(Name = "File Content", Prompt = "Please Give File Content")]
    public IFormFile XmlFile { get; set; }

    [Display(Name = "Previous Signed/Unsigned File", Prompt = "Please select Previous File")]
    public long? PreviousFileId { get; set; }
}

globals.gs: 我创建了一个名为Globals的工作表,在其中存储了一些工作表名称和电子表格的ID,然后在清除内容之前将主表复制到该工作表中。

type = "module"

Codes.gs: 我在这里有一个菜单设置。

function archiveDaily() {
  const mshsr=4;
  const ss=SpreadsheetApp.getActive();
  const msh=ss.getSheetByName('Master');
  const dss=SpreadsheetApp.openById(getGlobal('MasterSheetsLogId'));//open master archive
  const ts=Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "MM/dd/yy HH:mm:ss")
  msh.copyTo(dss).setName(ts);//copies current master sheet to master archive not sure if you would want this but I would
  if(msh.getLastRow()-mshsr+1>0) {
    msh.getRange(mshsr,1,msh.getLastRow()-mshsr+1,msh.getLastColumn()).clearContent();
  }
  const nA=getGlobal('DailySheetNames').split(',');//sheet name store in Globals sheet as a hash table
  const dt=new Date();
  const td=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();//start of day
  const to=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()+1).valueOf();//end of day
  const dshsr=4;
  nA.forEach(function(name,i){
    let dsh=ss.getSheetByName(name);
    let drg=dsh.getRange(dshsr,1,dsh.getLastRow()-dshsr+1,dsh.getLastColumn());
    let v=drg.getDisplayValues();
    let mv=[[dsh.getName(),'','','','','','','','','','','','','','','','']];//Display Sheetname on top of every group
    v.forEach(function(r,i){
      let idt=new Date(r[0]).valueOf();
      //Select rows that fall between midnights
      if(idt>td && idt<to) {
        mv.push(r);
      }
    });
    msh.getRange(msh.getLastRow()+1,1,mv.length,mv[0].length).setValues(mv);//Put each sheets data into master sheet
  });
}

function createDailyTrigger() {
  var ss=SpreadsheetApp.getActive();
  //This line keeps you  from creating more than one trigger
  if(!isTrigger('archiveDaily')) {
    ScriptApp.newTrigger('archiveDaily').timeBased().everyDays(1).atHour(23).create();
  }
}

其中一张纸的图像:

enter image description here

数据表之一的

csv:

//Filename: triggers.gs
function deleteTrigger(triggerName){
  var triggers=ScriptApp.getProjectTriggers();
  for (var i=0;i<triggers.length;i++){
    if (triggerName==triggers[i].getHandlerFunction()){
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }
}

function isTrigger(funcName){
  var r=false;
  if(funcName){
    var allTriggers=ScriptApp.getProjectTriggers();
    for(var i=0;i<allTriggers.length;i++){
      if(funcName==allTriggers[i].getHandlerFunction()){
        r=true;
        break;
      }
    }
  }
  return r;
}

function deleteAllTriggers(){
  var triggers=ScriptApp.getProjectTriggers();
  for (var i=0;i<triggers.length; i++){
    ScriptApp.deleteTrigger(triggers[i]);
  }
}

function displayProjectTriggers() {
  var tA=ScriptApp.getProjectTriggers();
  var html="<style>th,td{border:1px solid black;padding:2px;margin:2px;}</style><table><tr><th>Handler Function</th><th>Trigger Type</th><th>Unique ID</th><td>&nbsp;</td></tr>";
  for(var i=0;i<tA.length;i++) {
    html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td><td><input type="button" value="Delete" onClick="google.script.run.withSuccessHandler(function(){google.script.run.displayProjectTriggers();}).deleteTrigger(\'%s\');" /></td></tr>',tA[i].getHandlerFunction(),tA[i].getEventType(),tA[i].getUniqueId(),tA[i].getHandlerFunction());
  }
  html+='</table><br /><input type="button" value="Close" onClick="google.script.host.close();" />';
  var userInterface=HtmlService.createHtmlOutput(html).setWidth(800);
  SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Project Triggers');
}

母版纸的图片

enter image description here

用于主表的csv:

function getGlobals(){
  var ss=SpreadsheetApp.getActive();
  var sh=ss.getSheetByName('Globals');
  var rg=sh.getRange(1,1,sh.getLastRow(),2);
  var vA=rg.getValues();
  var g={};
  for(var i=0;i<vA.length;i++){
    g[vA[i][0]]=vA[i][1];
  }
  return g;
}

function setGlobals(dfltObj){
  var dfltH=Object.keys(dfltObj).length;
  if(dfltObj){
    var ss=SpreadsheetApp.getActive();
    var sh=ss.getSheetByName('Globals');
    var rg=sh.getRange(1,1,dfltH,2);
    var vA=rg.getValues();
    for(var i=0;i<dfltH;i++){
      vA[i][1]=dfltObj[vA[i][0]];
    }
    rg.setValues(vA);
  }
}

function getGlobal(name){
  return getGlobals()[name];
}

function setGlobal(name,value){
  var curObj=getGlobals();
  if(!curObj.hasOwnProperty(name)) {
    var ss=SpreadsheetApp.getActive();
    var sh=ss.getSheetByName('Globals');
    sh.appendRow([name,value])
  }else{
    curObj[name]=value;
    setGlobals(curObj);
  }
}

function cleanGlobals() {
  var ss=SpreadsheetApp.getActive();
  var sh=ss.getSheetByName('Globals');
  if(sh.getLastColumn()>2) {
    sh.getRange(1,3,sh.getLastRow(),sh.getLastColumn()-2).clearContent();
  }
  var rg=sh.getRange(1,1,sh.getLastRow(),2);
  var vA=rg.getValues();
  for(var i=0;i<vA.length;i++) {
    if(!vA[i][0] || !vA[i][1]) {
      var userInterface=HtmlService.createHtmlOutput('Globals Sheet Requires Maintenance...Do it know.' + ' Check Row ' + Number(i + 1));
      SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Global Maintenance Required');
      break;
    }
  }
}

答案 2 :(得分:1)

脚本的简化版本:

//@OnlyCurrentDoc
function merge() {
  const ss = SpreadsheetApp.getActive();
  const arr = ss
    .getSheets()
    .filter(s => !s.getName().includes('Master'))//exclude Master sheet
    .flatMap(s => s.getDataRange().getValues());//map sheet to values and flatten it
  ss.getSheetByName('Master')
    .getRange(1, 1, arr.length, arr[0].length)
    .setValues(arr);
}

已修改

  • 排除主表,而不包括所有项目表
  • 使用v8 es6语法和优化

答案 3 :(得分:1)

以下脚本执行以下操作:

1)不论其他标签的命名约定如何,都必须每日存档(仅主标签需要正确命名)

2)忽略其他选项卡中的标题行(假定主菜单具有标题行)

3)每天触发

4)新增了归档以前的母版的功能,以防脚本在访问数据之前运行-仅1天归档

EverydayTrigger函数创建每日循环,而MergeSheet函数是合并工作表的主要脚本。每一行脚本的解释都在脚本本身中。

function EverydayTrigger() {
  ScriptApp.newTrigger("MergeSheets")
  .timeBased()
  .everyDays(1)
  .create();
}

function MergeSheets() {
  var app = SpreadsheetApp;
  var ss = app.getActiveSpreadsheet();
  var data = null;
  var RetrieveSheet = null;
  var PasteSheet = ss.getSheetByName("Master");

  /*
  // Use this if you are using the archiving option
  var PrevArchSheetName = PasteSheet.getRange(1,1000).getValue();

  if(PrevArchSheetName.length > 1){
    var PrevArchSheet = ss.getSheetByName(PrevArchSheetName); //Retrieve the stored Archive Sheet's Name
    ss.deleteSheet(PrevArchSheet);
  }
  */

  var sheets = ss.getSheets();//get all sheets regardless of naming conventions (allow you to expand your data source)

  /*
  //This portion can be used to allow for archiving of the previous day data in case you still need it.
  var ArchiveSheet = ss.insertSheet(new Date() + "Master");
  PasteSheet.getDataRange().copyTo(ArchiveSheet.getRange(1,1));
  PasteSheet.getRange(1,1000).setValue(ArchiveSheet.getName()); // stores the name of the sheet so as to delete it when the script run again
  */

  PasteSheet.getRange(2,1,PasteSheet.getLastRow(),PasteSheet.getLastColumn()).clear();//removes all old information

  for (var i =0; i<sheets.length; i++){
    RetrieveSheet = ss.getSheetByName(sheets[i].getName());
    if (RetrieveSheet.getName() != 'Master'){
      //data = RetrieveSheet.getDataRange(); //use this if you want the header row for each salesperson
      data = RetrieveSheet.getRange(2,1,RetrieveSheet.getLastRow(),RetrieveSheet.getLastColumn()); //presuming you dont want the header rows
      data.copyTo(PasteSheet.getRange(parseInt(PasteSheet.getLastRow())+1,1));
    }
  }
}

答案 4 :(得分:0)

正如其他人所说,= query({},“ select * where col1!=”“)是对此的最佳解决方案。

此外,它通过API调用链接到我们的数据库。

感谢卡斯珀你的传奇!

自动化ftw!