2021年12月に、この記事(2次元配列を連想配列にするテクニック)に関する最新情報をブログにしました。

どうも。つじけ(tsujikenzo)です。このシリーズでは「便利な関数を使いまわす為にライブラリを活用しよう」についてお送りしております。
追記:「版の管理(新IDE)」20210108
前回のおさらい
前回はライブラリの基本をおさらいしました。「ここを見れば全て書いてるよ」というブログもご紹介させていただきました。
それでは先日の「2次元配列をオブジェクトに変換する」シリーズで作成した関数を、ライブラリ化して使いまわしてみたいと思います。
パーツ化した関数とは
コチラのシリーズでお送りしました「2次元配列をオブジェクトに変換する」関数ですが、
今一度おさらいのために全文を掲載します。
//グローバルエリア
const VALUES = [
['id','name','age'],
['tg001','辻健蔵',35],
['tg002','江藤大',37]
];
const HEADER = [...VALUES].shift();
//呼び出し
function callObjectChanger(){
const elementsArray = getElementsArray_();
console.log(elementsArray);
const elementsObject = getElementsObject_();
console.log(elementsObject);
}
/**
* オブジェクト群を配列で格納するパーツ
* @return {array} 配列 e.g[{id:’tg001’, name:’kenzo’},{},{}]
*/
function getElementsArray_() {
const elementsData = [];
for (const value of VALUES){
elementsData.push(getElementInfo_(value));
}
return elementsData;
}
/**
* オブジェクト群をオブジェクトで格納するパーツ
* @return {object} オブジェクト e.g{{id:’tg001’, name:’kenzo’},{},{}}
*/
function getElementsObject_() {
const elementsData = {};
for (const [i,value] of VALUES.entries()){
elementsData[VALUES[i][0]] = getElementInfo_(value);
}
return elementsData;
}
/**
* 1要素をオブジェクト化してくれるパーツ
* @param {array} values e.g [‘tg001’,’kenzo’]
* @return {object} オブジェクト e.g{id:’tg001’, name:’kenzo’}
*/
function getElementInfo_(value) {
const object = {};
for(const [i, element] of HEADER.entries()){
object[HEADER[i]] = value[i];
}
return object
}
このような構成になっていました。
- グローバル定数VALUES・・・2次元配列データ
- グローバル定数HEADER・・・VALUESのタイトル行を格納した変数
- 下記2つの関数を呼び出す関数・・・callObjectChanger()
- 配列もしくはオブジェクト型のオブジェクトを返す関数2つ
- -getElementsArray_()
- -getElementsObject_()
- 1要素をオブジェクト化してくれるパーツ・・・getElementInfo_(value)→関数内でHEADERを使用
自分のプロジェクトファイルにスクリプトファイルとしてコピペして使う分にはこの構成でも問題なさそうですが、ライブラリとして公開する為には少し精査が必要そうです。
ライブラリの下準備
呼び出し関数を追加
関数はこのままでも十分ですが、「2次元配列とあるユニークな値(概ねIDが適用される)を引数として渡すと、該当するレコードがあれば、オブジェクトを返す」という関数も用意したいと思います。
/**
* keyを渡すとオブジェクト化した1レコードを返すパーツ
* @param {string} key e.g ‘id001’)
* @return {object} オブジェクト e.g{id:’tg001’, name:’kenzo’}
*/
function getElementData(key) {
for (const value of VALUES) {
if (value.includes(key)) return getElementInfo_(value);
}
}
- 1要素をオブジェクト化で返してくれるパーツ・・・getElementData_(key)
ユーザーがアクセスする関数のおさらい
ライブラリを公開した後にユーザーが使う関数と関数内で必要な値をイメージします。
- getElementsArray_()・・・→関数内でVALUESを使用
- getElementsObject_()・・・→関数内でVALUESを使用
- getElementData(key)・・・→関数内でVALUESを使用
ユーザーは直接アクセスしませんが、下記の関数も必要でした。
- getElementInfo_(value)→関数内でHEADERを使用
つまり、これらの関数を使う為には、グローバルエリアに書いたVALUESとHEADERが必要です。ライブラリではグローバルエリアを使用するのは難しそうなので、別途、VALUESを取得する処理をパーツ化してしまいましょう。
VALUESを取得する関数を追加
ユーザーが操作することはありませんのでプライベート関数にしておきます。
/**
* idとシート名を渡すと2次元配列のVALUESを返すパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @return {object} 2次元配列 [[],[]])
*/
function getSheetValues_(id, name){
const VALUES =
SpreadsheetApp.openById(id).getSheetByName(name).getDataRange().getValues();
return VALUES;
}
ユーザーがアクセスする関数の改修
したがってライブラリ内の関数を動かす際は、関数内でgetSheetValues_(id, name)を実行するように変更します。このような変更です。(プライベート関数でしたがアンダースコアを取り除いてパブリック関数にします。)
getElementsArray_()・・・→関数内でVALUESを使用
getElementInfo_(value)・・→関数内でHEADERを使用
↓
getElementsArray(id, name)・・・→関数内でgetSheetValues_(id, name)を実行
getElementInfo_(header,value)・・→引数でheaderを渡す
実際に変更したコードはこのようになります。
/**
* オブジェクト群を配列で格納するパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @return {array} 配列 e.g[{id:’tg001’, name:’kenzo’},{},{}]
*/
function getElementsArray(id,name) {
const elementsData = [];
const values = getSheetValues_(id, name);
const header = [...values].shift();
for (const value of values){
elementsData.push(getElementInfo_(header,value));
}
return elementsData;
}
/**
* オブジェクト群をオブジェクトで格納するパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @return {object} オブジェクト e.g{{id:’tg001’, name:’kenzo’},{},{}}
*/
function getElementsObject(id,name) {
const elementsData = {};
const values = getSheetValues_(id, name);
const header = [...values].shift();
for (const [i,value] of values.entries()){
elementsData[values[i][0]] = getElementInfo_(header,value);
}
return elementsData;
}
/**
* SSID,シート名,keyを渡すとオブジェクト化した1レコードを返すパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @param {string} key e.g ‘id001’)
* @return {object} オブジェクト e.g{id:’tg001’, name:’kenzo’}
*/
function getElementData(id,name,key) {
const values = getSheetValues_(id, name);
const header = [...values].shift();
for (const value of values) {
if (value.includes(key)) return getElementInfo_(header,value);
}
}
【MEMO】グローバル変数を使わずに、プロパティストアに格納する手法がありますが、プロパティストアに格納できるデータは限りがあります(文字列だと全角4500文字程度)ので、ちょっとスペース不十分かなと言う気がします。
版の管理(旧IDE)
下準備が終わりましたら、スタンドアロンスクリプトを用意してコード全文をスクリプトファイルに張り付けて保存します。(コード全文は最下部に掲載します。)
プロジェクトファイルの名前は「SpreadsheetDataConverterToObject」としました。
一気に版の管理まで進みますが、ファイルメニューの[版を管理]をクリックして「ver1」のような名前を付けます。

ver1が出来上がったようです👏👏👏。今後はコードに変更がある度にバージョンをアップデートしていきます。

版の管理(新IDE)
下準備が終わりましたら、スタンドアロンスクリプトを用意してコード全文をスクリプトファイルに張り付けて保存します。(コード全文は最下部に掲載します。)
プロジェクトファイルの名前は「SpreadsheetDataConverterToObject」としました。
続いてバージョンの管理ですが、右上の[デプロイ]から[デプロイを管理]をクリックします。

初めてデプロイしますので[デプロイメントの作成]をクリックします。

「デプロイタイプを選択してください」とのことですので、歯車をクリックして[ライブラリ]をクリックします。

ユーザーにライブラリをインストールしてもらった時に表示される「バージョンの説明文」こちらにありますので、簡略的に「英語で」バージョン概要を書きます。最後に[デプロイ]をクリックします。

ライブラリのURLが発行されました。ユーザーにはURLに記述されている[スクリプトID]をお伝えすることになります。末尾にスラッシュと数字があることがありますので気をつけてください。これは不要です。
https://script.google.com/macros/library/d/{スクリプトID}/1

このプロジェクトの共有権限を[リンクを知っている全員]に変更します。(※この作業は不要かもしれません。もしスクリプトIDを入力してもライブラリが見つからないなどのエラーが出る場合はこちらの共有設定を疑ってみてください)

ライブラリのバージョンを上げる
ライブラリのバージョンを上げる際は、常に[新しいデプロイ]を行うことになります。

説明文には「バージョンが分かるように」としたのはこの為です。

[デプロイ]をクリックすると自動的に過去のバージョンはアーカイブされ、常に最新のバージョンがデプロイされている状態になります。
もちろんIDやURLに変更はありません。(過去のバージョンを削除する方法は不明です💦)

現在のバージョンをアップデート
現在デプロイされているバージョンをアップデートするには、[デプロイを管理]をクリックして[デプロイ]をクリックします。動作的には「新規デプロイ」も「デプロイの更新」も全て「デプロイ」一言で片付けられているので分かり辛いかもしれませんね💦
(※さらにライブラリのバージョンを更新した場合、クラウド上に反映されるには少し時間がかかりますが、その間に使用している、呼び出し側のプロジェクトファイルもリロードして読み込みしなおした方がいいです。)

以上で設定は終わりです。新IDEで作業を進めている方は、下記の[実際に使ってみる]までジャンプお願いします。
ライブラリとして公開する(旧IDE)
色々なやり方がありますが、共有設定の変更が楽だと思います。リンクを知っている全員を閲覧者として設定します。

実際にユーザーに伝えるのはスクリプトIDです。メニュー[ファイル]から[プロジェクトのプロパティ]を開いて。[プロジェクトキー]を確認しましょう。
今回公開するライブラリのプロジェクトキーはこちらです。
プロジェクトキー:MBzhjggcDiqmvnss9oreVp5lto6qNtfYu

実際に使ってみる
別のプロジェクトファイルでライブラリをインポートしてみます。(インポートの仕方は割愛しますね)無事に公開できているようです👏👏👏

適当なテーブルのスプレッドシートを用意して、idとシート名をライブラリに渡してみましょう。

今回は配列型のオブジェクト形式に変換してみたいと思います。
function getLibraryDate() {
const id = 'Spread Sheet ID';
const name = 'シート1';
const valuesObject = SpreadsheetDateConverterToObject.getElementsArray(id, name);
console.log(valuesObject);
}
では実行してログを確認してみましょう。

[{},{}]形式で取得できているみたいですね。我ながらちょっと感動してます👏👏👏
まとめ
さて、自作した関数をライブラリ化して、どこからでも呼び出せるようにしてみました。どうでもいいかもしれませんが、このコードの共有設定をしてプロジェクトキーを呼び出せば他の場所から呼び出せるってすごいことだと思います💦GASが環境構築など不要でローコードと言われる所以です。(これを他のプログラミング言語でやろうとするともっと大変です。)
次回は、このライブラリのマニュアルのようなものを少しまとめたいと思います。
コード全文
お待たせしましたwライブラリの中身です。リファクタリング是非お願いします!
/**
* オブジェクト群を配列で格納するパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @return {array} 配列 e.g[{id:’tg001’, name:’kenzo’},{},{}]
*/
function getElementsArray(id,name) {
const elementsData = [];
const values = getSheetValues_(id, name);
const header = [...values].shift();
for (const value of values){
elementsData.push(getElementInfo_(header,value));
}
return elementsData;
}
/**
* オブジェクト群をオブジェクトで格納するパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @return {object} オブジェクト e.g{{id:’tg001’, name:’kenzo’},{},{}}
*/
function getElementsObject(id,name) {
const elementsData = {};
const values = getSheetValues_(id, name);
const header = [...values].shift();
for (const [i,value] of values.entries()){
elementsData[values[i][0]] = getElementInfo_(header,value);
}
return elementsData;
}
/**
* SSID,シート名,keyを渡すとオブジェクト化した1レコードを返すパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @param {string} key e.g ‘id001’)
* @return {object} オブジェクト e.g{id:’tg001’, name:’kenzo’}
*/
function getElementData(id,name,key) {
const values = getSheetValues_(id, name);
const header = [...values].shift();
for (const value of values) {
if (value.includes(key)) return getElementInfo_(header,value);
}
}
//++プライベート関数ここから++//
/**
* 1要素をオブジェクト化してくれるパーツ
* @param {array} values e.g [‘tg001’,’kenzo’]
* @return {object} オブジェクト e.g{id:’tg001’, name:’kenzo’}
*/
function getElementInfo_(header,value) {
const object = {};
for(const [i, element] of header.entries()){
object[header[i]] = value[i];
}
return object
}
/**
* idとシート名を渡すと2次元配列のVALUESを返すパーツ
* @param {string} SpreadSheet ID)
* @param {string} Sheet Name e.g ‘シート1’)
* @return {object} 2次元配列 [[],[]])
*/
function getSheetValues_(id, name){
const values =
SpreadsheetApp.openById(id).getSheetByName(name).getDataRange().getValues();
return values;
}
//++プライベート関数ここまで++//