目的
GAS(Google Apps Script)でGoogleスプレッドシートのデータをsheet.getDataRange().getValues()
で2次元配列として取り出し後、加工してsetValues()
で書き戻す、というのはよくあると思う。
2次元配列のままでは列の追加や削除、入れ替えなどの変更に対して弱いのでこれまではObjectに変換してから処理していたのだが、Objectではキー(プロパティ)の順序が保証されない(=スプレッドシートに書き戻したときに列の順序が変わってしまう可能性がある)、ということを知ったので、順序が保証されるMapに変換することに。
ということで、1行目がヘッダである2次元配列と、Mapの配列を相互に変換する関数を作成した(V8エンジン限定)。
できたもの
/**
* 2次元配列(第1行はヘッダ)をMapの配列に変換
* @param {Array<Array>} table 第1行をヘッダとする2次元配列
* @return {Array<Map>} Mapの配列
*/
function tableToMaps(table){
const headers = table[0];
return table.slice(1).map((e) => e.reduce((i,n,a) => i.set(headers[a], n), new Map()));
}
/**
* Mapの配列を2次元配列(第1行はヘッダ)に変換
* @param {Array<Map>} maps Mapsの配列
* @return {Array<Array>} 第1行をヘッダとする2次元配列
*/
function mapsToTable(maps){
const headers = [...maps[0].keys()];
return [headers, ...maps.map((e) => (headers.map((h) => e.get(h))))]
}
エラー処理等はしてないので適当に呼び出し側で😁
例
2次元配列 → Mapの配列
const table = [
["col1", "col2", "col3", "col4"],
[1, 2, 3, 4],
[5, 6, 7, 8],
["a", "b", "c", "d"]
];
const maps = tableToMaps(table);
Mapの配列 → 2次元配列
mapsToTable(maps);
補足
##オブジェクトの配列との変換
ちなみにそれまで使っていたObjectの配列と2次元配列との相互変換は以下。
/**
* 2次元配列(第1行はヘッダ)をオブジェクトの配列に変換
* @param {Array<Array>} table 第1行をヘッダとする2次元配列
* @return {Array<Object>} オブジェクトの配列
*/
function tableToObjects(table){
const headers = table[0];
return table.slice(1).map((e) => e.reduce((i,n,a) => {i[headers[a]] = n; return i;}, {}));
}
/**
* オブジェクトの配列を2次元配列(第1行はヘッダ)に変換
* @param {Array<Object>} objects オブジェクトの配列
* @return {Array<Array>} 第1行をヘッダとする2次元配列
*/
function objectsToTable(objects){
const headers = Object.keys(objects[0]);
return [headers, ...objects.map((e) => (headers.map((h) => e[h])))];
}
ちなみに{i[headers[a]] = n; return i;}
を({...i, [headers[a]]: n})
と書いたら速度が1/3程度まで落ちた。