我需要根据列值将 Google Sheet 拆分为多个选项卡(工作表)

I need to split a Google Sheet into multiple tabs (sheets) based on column value

我搜索了很多可能的答案,但似乎找不到一个可行的答案。我有一个包含大约 1600 行的 Google Sheet,我需要根据标题为“房间”的列中的值将其分成大约 70 个不同的选项卡(每个选项卡中大约有 20-30 行)。我一直在排序,然后剪切和粘贴,但是对于 70 多个标签,这非常乏味。

我可以使用查询功能,但我仍然需要创建一个新选项卡,粘贴该功能并更新该特定选项卡的参数。

这个脚本看起来很接近:

ss = SpreadsheetApp.getActiveSpreadsheet();
itemName = 0;
itemDescription = 1;
image = 2;
purchasedBy = 3;
cost = 4;
room = 5;
isSharing = 6;
masterSheetName = "Master";

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Update Purchases')
      .addItem('Add All Rows To Sheets', 'addAllRowsToSheets')
      .addItem('Add Current Row To Sheet', 'addRowToNewSheet')
      .addToUi();
}

function addRowToNewSheet() {
  var s = ss.getActiveSheet();
  var cell = s.getActiveCell();
  var rowId = cell.getRow();
  var range = s.getRange(rowId, 1, 1, s.getLastColumn());
  var values = range.getValues()[0];
  var roomName = values[room];
  appendDataToSheet(s, rowId, values, roomName);
}

function addAllRowsToSheets(){
  var s = ss.getActiveSheet();
  var dataValues = s.getRange(2, 1, s.getLastRow()-1, s.getLastColumn()).getValues();
  for(var i = 0; i < dataValues.length; i++){
    var values = dataValues[i];
    var rowId = 2 + i;
    var roomName = values[room];
    try{
      appendDataToSheet(s, rowId, values, roomName);
    }catch(err){};
  }
}

function appendDataToSheet(s, rowId, data, roomName){
  if(s.getName() != masterSheetName){
    throw new Error("Can only add rows from 'Master' sheet - make sure sheet name is 'Master'");
  }
  var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
  var roomSheet;
  if(sheetNames.indexOf(roomName) > -1){
    roomSheet = ss.getSheetByName(roomName);
    var rowIdValues = roomSheet.getRange(2, 1, roomSheet.getLastRow()-1, 1).getValues();
    for(var i = 0; i < rowIdValues.length; i++){
      if(rowIdValues[i] == rowId){
        throw new Error( data[itemName] + " from row " + rowId + " already exists in sheet " + roomName + ".");
        return;
      }
    }
  }else{
    roomSheet = ss.insertSheet(roomName);
    var numCols = s.getLastColumn();
    roomSheet.getRange(1, 1).setValue("Row Id");
    s.getRange(1, 1, 1, numCols).copyValuesToRange(roomSheet, 2, numCols+1, 1, 1);
  }
  var rowIdArray = [rowId];
  var updatedArray = rowIdArray.concat(data);
  roomSheet.appendRow(updatedArray);
}

但我总是在第 51 或 52 行收到意外的标记错误:

var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())]; 

(显然,列名等不一定适合我的数据,我尝试更改它们以匹配我的需要。不确定这是否是问题的一部分。)

这是我的数据示例:https://docs.google.com/spreadsheets/d/1kpD88_wEA5YFh5DMMkubsTnFHeNxRQL-njd9Mv-C_lc/edit?usp=sharing 这应该return两个独立的tabs/sheets基于房间。

我显然不是程序员,不知道 Visual Basic 或 Java 或任何东西。我只知道如何 google 和复制东西....令人惊讶的是我经常让它工作。

如果您能提供帮助,请告诉我您还需要什么。

试试下面的代码:

  • 'splitSheetIntoTabs' 会将您的主 sheet 分成 sheet,每行 30 行。它只会复制内容而不是背景颜色等。
  • 'deleteTabsOtherThanMaster' 将还原 'splitSheetIntoTabs' 所做的更改。此功能将有助于恢复 splitSheetIntoTabs 所做的更改。

function splitSheetIntoTabs() {
   var sheet = SpreadsheetApp.getActiveSheet();
  var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
  var header = rows[0];
  var contents = rows.slice(1);
  var totalRowsPerSheet = 30; // This value will change no of rows per sheet
  
  //below we are chunking the toltal row we have into 30 rows each
  var contentRowsPerSheet = contents.map( function(e,i){ 
     return i%totalRowsPerSheet===0 ? contents.slice(i,i+totalRowsPerSheet) : null; 
}).filter(function(e){ return e; });

  contentRowsPerSheet.forEach(function(e){
    //crate new sheet here
  var currSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
    
    //append the header
    currSheet.appendRow(header);
    
    //populate the rows
    e.forEach(function(val){
      currSheet.appendRow(val);
    });
    
  });
  

}


// use this function revert the sheets create by splitSheetIntoTabs()

function deleteTabsOtherThanMaster() {
  var sheetNotToDelete ='Master';
    var ss = SpreadsheetApp.getActive();
  
  
    ss.getSheets().forEach(function(sheet){
      if(sheet.getSheetName()!== sheetNotToDelete)
      {
        ss.deleteSheet(sheet);
      }
    });


}

我已经完成了这个脚本,它成功地获取了每个房间并使用相应的房间名称创建了一个新的 sheet 并添加了具有相同房间的所有行。

function myFunction() {
  var sheet = SpreadsheetApp.getActiveSheet();

  // This var will contain all the values from column C -> Room
  var columnRoom = sheet.getRange("C:C").getValues();

  // This var will contain all the rows
  var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();

  //Set the first row as the header
  var header = rows[0];

  //Store the rooms already created
  var completedRooms = []

  //The last created room
  var last = columnRoom[1][0]


  for (var i = 1; i < columnRoom.length; i++) {    

    //Check if the room is already done, if not go in and create the sheet
    if(!completedRooms.includes(columnRoom[i][0])) {

      //Set the Sheet name = room (except if there is no name, then = No Room)
      if (columnRoom[i][0] === "") {
        var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("No Room");
      } else {
        var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(columnRoom[i][0]);
      }


      //append the header
      currentSheet.appendRow(header);
      currentSheet.appendRow(rows[i]);
      completedRooms.push(columnRoom[i][0])
      last = columnRoom[i][0]
    } else if (last == columnRoom[i][0]) {

    // If the room's sheet is created append the row to the sheet


      var currentSheet = SpreadsheetApp.getActiveSpreadsheet()
      currentSheet.appendRow(rows[i]);
    }

  }

}

请测试它,不要犹豫,评论改进。

我使用的是 Kessy 的好脚本,但当数据变得非常大时,脚本超时,开始出现问题。我开始寻找方法来减少脚本 read/wrote 到电子表格的次数(而不是一次 read/write 一行)并找到了这个 post

利用这一原则并将脚本中的循环更改为循环内循环有助于减少这些调用。这意味着您还可以避免第二次调用追加行(“else”)。我的脚本与示例略有不同,但基本上以这样的方式结束:

`for (var i = 1; i < theEmails.length; i++) {    
//Ignore blank Emails and sheets created
if (theEmails[i][0] !== "" && !completedSheets.includes(theEmails[i][0])) { 

  //Set the Sheet name = email address.  Index the sheets so they appear last.

    var currentSheet = theWorkbook.insertSheet(theEmails[i][0],4+i);
  
  //append the header
  
  //To avoid pasting formulas, we have to paste contents
  headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});

  //Now here find all the rows containing the same email address and append them
  var theNewRows =[];
  var b=0;
  for(var j = 1; j < rows.length; j++)
{
 
if(rows[j][0] == theEmails[i][0]) {
  
  theNewRows[b]=[];//Initial new array
  theNewRows[b].push(rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7]);
  b++;
}
}var outrng = currentSheet.getRange(2,1,theNewRows.length,8); //Make the output range the same size as the output array
outrng.setValues(theNewRows);

我发现约 1000 行的 table 超时,但新脚本用了 6.5 秒。它可能不是很整洁,因为我只涉足脚本,但也许它有帮助。