使用Apps脚本转置并复制到另一个工作表

时间:2018-10-10 08:29:57

标签: google-apps-script google-sheets

我需要使用Apps Script根据合并的标头将列数据转置为行。 下面是我的输入和预期输出的视图,

输入

输出

Sample sheet

到目前为止我写的是什么

function myFunction() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var range = sheet.getRange("A1:AO1");

  var mergedValues = [];

  //get the header added to the array
  mergedValues.push(sheet.getRange("A2:I2").getValues());
  Logger.log(mergedValues);

  var mergedRanges = range.getMergedRanges();
  for (var i = 0; i < mergedRanges.length; i++) {
    var calcA1Notation = "A"+(i+3) + ":C"+(i+3);
    var monA1Notation = "D"+(i+3) + ":F"+(i+3);

    //Load the Transpose values into the array
    mergedValues.push([[
      sheet.getRange(calcA1Notation).getValues().toString(),
      mergedRanges[i].getDisplayValue(),
      sheet.getRange(monA1Notation).getValues().toString()
    ]]);
  }
  Logger.log(mergedValues[0].length);

  for (var i = 0; i < mergedValues.length; i++){
    //Writes to the lastrow+1 of the sheet
    sheet.getRange(sheet.getLastRow()+1, 1).setValue(mergedValues[i]);
  }
} 

你们能帮我修改Google脚本以产生预期的结果吗?

1 个答案:

答案 0 :(得分:0)

问题中包含“移调”一词,但这具有误导性。
发问者的目标是直截了当的;将单元格从一张复制到另一张。附带一个条件,将一张表中的列标题作为单元格包含在目标范围内。

发问者演示了代码,尽管他们没有解释这在什么程度上是有目的的。该代码接收三列数据,并将值连接到单个单元格中。充其量,您可能会将此视为早期草案。

对源数据的引用并不复杂;获得月份名称是主要的麻烦。我使用两个循环遍历“源”表上的行,因为发问者的预期结果是数据应按月排序。

我本可以构建一个例程来将月份字符串值转换为数字值,然后对该值进行排序(我当然考虑过)-但我没有;)

月份名称以大写字母表示,发问者的结果使用TitleCase。同样,我可以构建一个例程来转换大小写,并且确实花了一些时间进行尝试。但是最后我决定这不是一个高度优先的事情。

function so5273586002() {

    var ss = SpreadsheetApp.getActiveSpreadsheet();

    // Declare the two sheets
    var sourcesheet = ss.getSheetByName("Input");
    var targetsheet = ss.getSheetByName("Output");

    // Get the respective starting row and ending rows.'' the target last row is declared in the loop.  
    var sourcestartrow = 3;
    var targetstartrow = 2;
    var sourcelastrow = sourcesheet.getLastRow();


    // get the the data
    var sourcerange = sourcesheet.getDataRange();
    var sourcevalues = sourcerange.getValues();


    // rubric for copying data.
    // each row of the source must create two rows in the target - one row for each month
    // the first three columns are repeats on both rows
    // each row includes the source data as well as the month name

    // target row #1
    // source columns A, B & C to target A,B,C
    // Month#1; value in D1 Source=> Target Column D (4)
    // source columns DEF to target E F G
    // target row #2
    // source columns A, B & C to target A,B,C
    // Month#2: value in G1 Source=> Target D (4)
    // source fields G, H I  to target E F G

    // the questioner's prefered layout is that all the rows are sorted by month; to achive this, I used two loops
    // the first to do the first month; the second to do the second month
    for (i = sourcestartrow; i < (sourcelastrow + 1); i++) {

        // get the last row for the target 
        var targetlastrow = targetsheet.getLastRow();

        // Columns A, B and C -> Columns A, B and C
        var targetRange = targetsheet.getRange(targetlastrow + 1, 1); //target: column =A, row = lastrow plus one
        var sourcetest = sourcesheet.getRange(i, 1, 1, 3).copyTo(targetRange); // range = active row, column=A, 1 row, 3 columns, copy to SheetTracker
        //Logger.log("source range is "+sourcesheet.getRange(i, 1, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 1).getA1Notation());//DEBUG

        // Month Name from the header
        var targetRange = targetsheet.getRange(targetlastrow + 1, 4); //target: column =D, (month) row = lastrow plus one
        var sourcetest = sourcesheet.getRange(1, 4).copyTo(targetRange, {
            contentsOnly: true
        }); // range = active row, column=A, 1 row, 3 columns, copy to SheetTracker
        // Logger.log("source range is "+sourcesheet.getRange(1, 4).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 4).getA1Notation());//DEBUG

        // Month details
        // Columns D E and F -> Columns E F and G
        var targetRange = targetsheet.getRange(targetlastrow + 1, 5); //target: column =E, row = lastrow plus one
        var sourcetest = sourcesheet.getRange(i, 4, 1, 3).copyTo(targetRange, {
            contentsOnly: true
        }); // range = active row, column=D(4), 1 row, 3 columns, copy to SheetTracker
        // Logger.log("source range is "+sourcesheet.getRange(i, 4, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 5).getA1Notation());//DEBUG

    } // end loop#1


    //Loop#2 to generate rows for the second month
    for (i = sourcestartrow; i < (sourcelastrow + 1); i++) {

        // get the last row for the target 
        var targetlastrow = targetsheet.getLastRow();

        // Columns A, B and C -> Columns A, B and C
        var targetRange = targetsheet.getRange(targetlastrow + 1, 1); //target: column =A, row = lastrow plus one
        var sourcetest = sourcesheet.getRange(i, 1, 1, 3).copyTo(targetRange); // range = active row, column=A, 1 row, 3 columns, copy to SheetTracker
        //Logger.log("source range is "+sourcesheet.getRange(i, 1, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 1).getA1Notation());//DEBUG

        // Month Name from the header
        var targetRange = targetsheet.getRange(targetlastrow + 1, 4); //target: column =D, (month) row = lastrow plus one
        var sourcetest = sourcesheet.getRange(1, 7).copyTo(targetRange, {
            contentsOnly: true
        }); // range = active row, column=G, 1 row, 3 columns, copy to SheetTracker
        //Logger.log("source range is "+sourcesheet.getRange(1, 7).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 4).getA1Notation());//DEBUG

        // Month details
        // Columns G H and I -> Columns E F and G
        var targetRange = targetsheet.getRange(targetlastrow + 1, 5); //target: column =E, row = lastrow plus one
        var sourcetest = sourcesheet.getRange(i, 7, 1, 3).copyTo(targetRange, {
            contentsOnly: true
        }); // range = active row, column=D(4), 1 row, 3 columns, copy to SheetTracker
        // Logger.log("source range is "+sourcesheet.getRange(i, 7, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 5).getA1Notation());//DEBUG
    } // end loop#2

}

此屏幕截图显示了源工作表(“输入”)。
Source sheet


这些屏幕截图显示了运行代码之前和之后的目标表(“输出”)。
Target sheet - Before

Target sheet - After


更新

如我的评论所述,早期的草案缺少两点:
1)它效率低下并且遵循不良做法,因为它在创建每个字段时都会记录其值。更合适的方法是将数据写入数组,然后在逐行处理完成后将数组复制到目标范围。
2)该代码由两个循环组成,以适应演示数据中的2个月。但是,这是不切实际的结果,因为实际上每一行中可能会有任意数量的几个月的数据。再次,这是一种糟糕的做法,当时更合适的方法是假设任意数量的月数据。更为有效的方法是在遍历每一行时构建数据数组。

此修订版克服了两个缺点。
另外,由于月份名称没有按照任何有意义的顺序排序,因此我添加了一个数字月份ID,可用于在输出数据表中进行过滤和排序。

function so5273586003() {

    var ss = SpreadsheetApp.getActiveSpreadsheet();

    // Declare the two sheets
    var sourcesheet = ss.getSheetByName("Input");
    var targetsheet = ss.getSheetByName("Output");

    // Get the respective starting row and ending rows.'' the target last row is declared in the loop.  
    var targetstartrow = 2;
    var sourcestartrow = 2;
    var sourcelastrow = sourcesheet.getLastRow();
    var sourcelastcolumn = sourcesheet.getLastColumn();
    //Logger.log("the last row is "+sourcelastow+", and the last column is "+sourcelastcolumn);

    // get the the data
    var sourcerange = sourcesheet.getDataRange();
    var sourcevalues = sourcerange.getValues();
    var sourcelength = sourcevalues.length;

    var i = 0;
    var m = 0;
    var month = 1;
    var dataarray = [];
    var masterarray = [];

    // start loop by row
    for (i = sourcestartrow; i < (sourcelastrow); i++) {


        // start loop by month (within row)
        for (m = 0; m <= (sourcelastcolumn - 6); m = m + 3) {

            dataarray = [];

            // add first three columns
            dataarray.push(sourcevalues[i][0]);
            dataarray.push(sourcevalues[i][1]);
            dataarray.push(sourcevalues[i][2]);

            //add the month name
            dataarray.push(sourcevalues[0][3 + m]);

            //add month data
            dataarray.push(sourcevalues[i][3 + m]);
            dataarray.push(sourcevalues[i][4 + m]);
            dataarray.push(sourcevalues[i][5 + m]);

            //create month id
            switch (sourcevalues[0][3 + m]) {
                case "JULY":
                    month = 1;
                    break;
                case "AUGUST":
                    month = 2;
                    break;
                case "SEPTEMBER":
                    month = 3;
                    break;
                case "OCTOBER":
                    month = 4;
                    break;
                case "NOVEMBER":
                    month = 5;
                    break;
                case "DECEMBER":
                    month = 6;
                    break;
                case "JANUARY":
                    month = 7;
                    break;
                case "FEBRUARY":
                    month = 8;
                    break;
                case "MARCH":
                    month = 9;
                    break;
                case "APRIL":
                    month = 10;
                    break;
                case "MAY":
                    month = 11;
                    break;
                case "JUNE":
                    month = 12;
                    break;
                default:
                    month = 100;
                    break;

            } // end switch

            // add the month id to the array (used for sorting)
            dataarray.push(month);

            // add the data to the master array before zeroing for next month
            masterarray.push(dataarray);

        } // months loop

    } // end row loop

    // get the length of the master array
    var masterlength = masterarray.length;
    // define the target range
    var TargetRange = targetsheet.getRange(targetstartrow, 1, masterlength, 8);
    // set the array values on the Target sheet
    TargetRange.setValues(masterarray);

}