みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。
「初心者でもわかるGoogle Apps Scriptのクラス」をテーマにシリーズ連載をお送りしております。
前回の記事はこちら。
自作オブジェクトについてプロパティを追加したり削除したりするメソッドの作り方をお伝えしました。
さて、その前回の記事でメソッドを作ったわけですが、そのおかげでfor~in文のループ対象にメソッドも入っちゃうという困ったことが起きちゃいます。
今回は、それをObject.keysメソッドを使って回避する方法をお伝えしていきます。
ということで、Google Apps Scriptでオブジェクトに直接追加しているプロパティのみをループする方法です。
では、行ってみましょう!
前回のおさらい
まず、対象としているスプレッドシートは以下のようなものです。
まず、この一行ずつのデータを表すクラスを作りましたが、それが以下のコードです。
(function(global){ var Person = function(record){ var _id = record[0]; this.name = record[1]; this.gender = record[2]; this.birthday = record[3]; Object.defineProperties(this, { id: { get: function(){ return _id; } } }); }; Person.prototype.greet = function(){ Browser.msgBox(this.name + "です、こんにちは!"); }; Person.prototype.log = function(){ Logger.log('%s|%s|%s|%s|', this.id, this.name, this.gender, this.birthday); }; global.Person = Person; })(this);
各フィールドを格納するプロパティと、いくつかの単純なメソッドで構成されています。
そして、スプレッドシートにはそのデータが複数行ありますので、それをオブジェクトを使った集合として取り扱うべく、もう一つクラスを作りました。
(function(global){ var Persons = function() { var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); values.shift(); for(var i = 0; i < values.length; i++){ var p = new Person(values[i]); this[p.id] = p; } }; Persons.prototype.add = function(record){ var p = new Person(record); this[p.id] = p; return this[p.id]; }; Persons.prototype.remove = function(id){ delete this[id]; }; global.Persons = Persons; })(this);
このクラスPersonsについて、要素を追加するaddメソッドと、要素を削除するdeleteメソッドを追加したのが前回でした。
メソッドもプロパティであることから起こる問題
さて、このクラスPersonsですが、生成したインスタンスにはidをプロパティに、Personオブジェクトを値に、データを集合として扱うオブジェクトのもとになります。
ですが、上記2つのメソッドを追加したことで、ちょっと困ったことが既に起こっています。
以下のようなスクリプトを実行すると、どのような困ったことが起きているかがわかります。
function myFunction() { var persons = new Persons(); for(var id in persons){ persons[id].log(); } }
スプレッドシートからデータを取得し、Personsクラスのインスタンスを作ります。
そして、その内容をfor~in文を使ってログ出力しようとするものです。
ただ、実行すると…
このように、「TypeError: オブジェクト function (record) {...} で関数 log が見つかりません。」というエラーが出てしまうのです。
原因はわかりますか?
for~in文のループはメソッドも対象になる
for~in文は、オブジェクト内のすべてのプロパティを取り出してループをするステートメントです。
今回の例でいうと、以下の3つがPersonsオブジェクトのメンバーとして期待しちゃいますよね。
- プロパティa01: BobのPersonオブジェクト
- プロパティa02: TomのPersonオブジェクト
- プロパティa03: IvyのPersonオブジェクト
ただ、思い出してください。
プロパティに関数を格納したものが、メソッドでしたよね。
てことは、以下の2つのメソッドもfor~in文のループの対象となります。
- プロパティadd: add関数の処理
- プロパティremove: remove関数の処理
これらの値は、Personオブジェクトではありませんから、logメソッドを実行できない…したがってエラーが出るというわけです。
オブジェクトに直接追加したプロパティのみをループする
それで、どうするか…ということなのですが、addメソッドもremoveメソッドも、オブジェクトに直接追加しているメソッドではなく、コンストラクタのprototypeプロパティに追加しているという点に注目します。
オブジェクトに直接追加しているプロパティだけ取り出す便利なメソッドがありまして、それがObject.keysメソッドです。
以下のように記述することで、指定したオブジェクトの直接追加しているプロパティのみを配列で取得します。
ちなみに、以下の記事で以前お伝えしたdefinePropertiesメソッドによるプロパティは除外されます。
純粋に直接追加されたプロパティだけを抽出してくれるんですね。
Object.keysメソッドを使ったループ
では、早速使用してみましょう。
Object.keysメソッドは配列を返しますので、forEachメソッドでループできますね。
関数myFunctionを以下のように変更しました。
function myFunction() { var persons = new Persons(); Object.keys(persons).forEach( function(id) { persons[id].log(); } ); }
実行すると、エラーも発生せず、以下のように正しいログ出力を得ることができました。
まとめ
以上、Google Apps Scriptでオブジェクトに直接追加しているプロパティのみをループする方法をお伝えしました。
オブジェクトのループは、for~in文だけというわけにはいかないんですね。
勉強になります。
では、次回はループしてログ出力する処理をメソッド化していきます。
どうぞお楽しみに!