如何在 google 应用程序脚本中 'melt' 宽 table/array?

How to 'melt' a wide table/array in google app script?

我在 Google 工作表中有一个 array/table,数据格式如下:

Catergory1 A A A A A B B B B B C C C C C
Catergory2 Q1 Q2 Q3 Q4 Q5 Q1 Q2 Q3 Q4 Q5 Q1 Q2 Q3 Q4 Q5
Propotions 34% 66% 9% 49% 31% 36% 69% 1% 10% 20% 98% 38% 21% 57% 76%

我想使用 Google App Script 将此数组转换为以下格式:

Catergory1 Q1 Q2 Q3 Q4 Q5
A 34% 66% 9% 49% 31%
B 36% 69% 1% 10% 20%
C 98% 38% 21% 57% 76%

提前致谢。

应用脚本

试试这个

function myFunction(){
  var ss = SpreadsheetApp.getActiveSpreadsheet()
  var sh = ss.getActiveSheet()
  var values = sh.getRange(1,2,3,sh.getLastColumn()-1).getValues()
  var rows = Array.from(new Set(values[0])).sort(); 
  var cols = Array.from(new Set(values[1])).sort();
  var result = Array.from({ length: rows.length }, () => Array.from({ length: cols.length }, () => ''));
  for (var i=0;i<sh.getLastColumn()-1;i++){
    result[rows.indexOf(values[0][i])][cols.indexOf(values[1][i])] = values[2][i]   
  }
  sh.getRange(6,1,rows.length,1).setValues(transpose([rows]))
  sh.getRange(5,2,1,cols.length).setValues([cols])
  sh.getRange(6,2,rows.length,cols.length).setValues(result)
}
function transpose(a){
  return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}

公式

=query(TRANSPOSE(B1:3),"select Col1, sum(Col3) where Col1 is not null group by Col1 pivot Col2")

这是我的变体:

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

  // get the data (without first column)
  var data = sheet.getDataRange().getDisplayValues().map(x => x.slice(1));

  // get rows and columns names
  var rows = Array.from(new Set(data[0])); // A, B, C...
  var cols = Array.from(new Set(data[1])); // Q1, Q2, Q3...
  
  // create the empty table
  var table = Array(rows.length).fill('')
    .map(x => x = Array(cols.length).fill(''));

  // fill the table with values from third row of data ('Proportions')
  for (let i in data[0]) {
    let row = rows.indexOf(data[0][i]);
    let col = cols.indexOf(data[1][i]);
    table[row][col] = data[2][i];
  }

  // add the header and first column (A,B,C...) to the table 
  var header = ['Category', ...cols];
  var body = table.map((x,i) => [rows[i], ...x]);
  table = [header, ...body];

  // put the table on the sheet
  sheet.clear().getRange(1,1,table.length,table[0].length).setValues(table);

  // set font of the header to 'Bold'
  sheet.getRange(1,1,1,sheet.getLastColumn()).setFontWeight('Bold');
}

它应该适用于任意数量的类别和字母。而且它甚至不需要对列进行排序,它们可以像:Q1、Q3、Q1、Q2...等等

如果您的数据总是经过排序,则脚本可以更简单一些并且可能更高效:

function myFunction2() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange().getDisplayValues();
  
  var letters = Array.from(new Set(data[0].slice(1)));    // A,B,C
  var categories = Array.from(new Set(data[1].slice(1))); // Q1,Q2,Q3,Q4,Q5
  var proportions = data[2].slice(1);                     // %,%,%...
 
  // make a table from the proportions
  var table = [];
  var rows = letters.length
  while (rows--) {
    var row = [];
    var cols = categories.length;
    while (cols--) row.push(proportions.shift());
    table.push(row);
  }
  
  // add a header and first column to the table
  var header = ['Categories', ...categories];
  var body = table.map(x => [letters.shift(), ...x]);
  table = [header, ...body];
  
  // put the table on the sheet
  sheet.clear().getRange(1,1,table.length,table[0].length).setValues(table);
}