
こんにちは、etau です 🤓
情報の収集と学習のために『ノンプログラマーのためのスキルアップ研究会』に所属しています。
今日はそのノンプロ研の中でもよく議論されるスプレッドシートの値をオブジェクト化する方法についてまとめていきたいと思います。
さて、以前は「配列があればオブジェクトなんて必要ない」と豪語していた私ですが、現在は Google Apps Script の中で押さえておきたいランキング No.1 です。
それでは、さっそくはじめていきます。
作りたいオブジェクト
例として、このような値が入力されたスプレッドシートを用意します。

上記のスプレッドシートの値から、1 行目の見出しをプロパティとした、行 (レコード) 単位のオブジェクトを配列化した、以下のような objects を生成する処理を書きたいと思います。
[ { name: 'Bob', age: 10, favorite: 'coffee' }, { name: 'Tom', age: 20, favorite: 'beer' }, { name: 'Ivy', age: 30, favorite: 'applepie' } ];
配列とオブジェクトの比較
まずはじめに、配列を利用する場合とオブジェクトを利用する場合のメリットとデメリットをまとめます。
配列を利用する場合のメリットとデメリット
メリット
- スプレッドシートの値を 2 次元配列として取得でき、すべての値にインデックスを利用してアクセスできる
デメリット
- インデックスで要素を指定するため、マジック ナンバーの温床になりやすい
- 列を追加された場合に、コードの書き直しが必要になる
オブジェクトを利用した場合のメリットとデメリット
メリット
- プロパティを利用してオブジェクトから値を取得するため、説明変数への代入が省略できる
- タイトル行を利用してプロパティを設定することによって、スプレッドシート側で列を追加されても対応できる
デメリット
- スプレッドシートの値を配列化したものからオブジェクトを生成するため、コード量が増える
オブジェクトを利用するデメリットはコード量が増えてしまうという点です。
というわけで、ここをスッキリ書くことでオブジェクトのデメリットをなくしていきましょう。
実際にオブジェクトを作る
スプレッドシートにシートが 1 つの設定で、コードを書いていきます。
スプレッドシート上の値を取得し、分割代入を使って見出し行を headers に、それ以下の値を残余引数を利用して records に代入します。
const values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); const [headers, ...records] = values;
headers は 1 次元配列です
for…of 文を利用したオブジェクトの作成
さて、まずは for…of 文を利用して、オブジェクトを作っていきます。
function myFunction01() { const values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); const [headers, ...records] = values; const objects = []; for (const record of records) { const [name, age, favorite] = record; const object = {}; object.name = name; object.age = age; object.favorite = favorite; objects.push(object); } }
これで完成ですが、見出しの項目 (列) 数が増減した場合に、8 行目の分割代入を更新しなければ対応できません 。
タイトル行から自動的にプロパティ名を設定していきたいので、for…of 文のネストと entries メソッドを利用して、対応していきます。
for…of 文と entries メソッドを利用したオブジェクトの作成
ネストされた for…of 文でオブジェクトのプロパティを 1 つずつ object に設定していきます。
function myFunction02() { const values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); const [headers, ...records] = values; const objects = []; for (const record of records) { const object = {}; for (const [i, value] of record.entries()) { object[headers[i]] = value; } objects.push(object); } }
これで、目的としている処理は、一通りできています。
列の情報に変化があっても、タイトル行の値 (headers) をプロパティとしたオブジェクトが生成されます。
続いて 2 つの for…of 文を、反復メソッドの map メソッドと forEach メソッドに置き換えてコードを書いてみます。
map メソッドと forEach メソッドを利用したオブジェクトの作成
1 つめを map メソッドに、2 つめを forEach メソッドに置き換えて entries メソッドで取得していたインデックスもよびだされるコールバック関数の第 2 引数を利用する方法に変更します。
function myFunction03() { const values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); const [headers, ...records] = values; const objects = records.map(record => { const object = {}; record.forEach((value, i) => object[headers[i]] = value); return object; }); }
map メソッドと reduce メソッドを利用したオブジェクトの作成
ここまでくると、かなり見た目がスッキリしてきます。
ですが、空の objects 宣言が目についてしまうので reduce メソッドを使って objects を宣言せずに、第 2 引数 の initialValue を利用する方法で書きかえてみます。
function myFunction04() { const values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); const [headers, ...records] = values; const objects = records.map(record => record. reduce((pre, cur, i) => { pre[headers[i]] = cur; return pre; }, {}) ); }
スッキリ書くことを目標としていたのに、なぜかコードがさらにゴツくなってしまいました。
アプローチを変えるために MDN へ旅に出ます。
map メソッドと fromEntries メソッドを利用したオブジェクトの作成
fromEntries メソッドに Array から Object への変換 というそのままの項目がありましたが、このままだとプロパティ名がインデックス番号になってしまうので、少し手を加えてみました。
function myFunction05() { const sheet = SpreadsheetApp.getActiveSpreadsheet(); const values = sheet.getDataRange().getValues(); const [headers, ...records] = values; const objects = records.map(record => Object.fromEntries(record. map((value, i) => [headers[i], value]) )); }
すばらしいスッキリ感です。
まとめ
スプレッドシートの値からオブジェクトを作っていく方法を説明してきました。
オブジェクトのコード量が増えるデメリットは、最後の fromEntries メソッドを使ったコードをテンプレートとして使うことによって、無に帰しました。
最初は使い慣れないかもしれませんが、メリットたくさんのオブジェクトにもぜひ触れてみてください。