どうも。つじけ(tsujikenzo)です。このシリーズでは 「会計freeeで100件以上の取引を取得しよう」 ということで全3回でお送りします。今日は1回目です。
新しいシリーズのようですが、先日の「会計freeeAPIを使って残高確認表を作成しよう」 の続きになっています。
先日の記事は、かなりバタバタしてしまったので、説明が足りなかった点の補足も加えながらお届けしたいと思います。
前回の連載おさらい
会計freeeの「取引(収入/支出)」では、1レコード=1取引というレコードと、 1レコードに複数の取引がぶら下がっているレコード があります。
また、取引の中には、[payments]プロパティがないものも含まれており。 レコードによってフィールド数が違う という状況が発生してしまっています。
レコードによってフィールドの数が違うということは、 データの一括処理ができなくなり、管理が大変 です。
前回の連載では、「いろいろな書き方があると思いますが、」という一言で片付けてしまいましたので、こちらを詳しく解説したいと思います。
1回目のタイトルは 「複雑な処理は関数を切り分けよう」 です。
アジェンダ
- 関数の切り分け方
- Try…Catch構文
- スプレッド構文とmapメソッド
関数の切り分け方
構造化データ
今回、必要な要件定義のイメージは、下記の通りです。
– 要素に配列が含まれているレコードがある
– 配列の要素数が合わないレコードがある
このような元のデータは、まず要素数を合わせる必要があります。要素数が揃っているデータを 「構造化データ」 と呼びます。
構造化データは、人間にとって目で見辛いかもしれませんが、パソコンにとっては処理がしやすい 構造です。
//元の配列
[
['a','b',[1,2,3]],
['a',[4,5,6]],
['a','b',[7]]
]
//加工後の配列=構造化データ
[
['a','b',1],
['a','b',2],
['a','b',3],
['a','',4],
['a','',5],
['a','',6],
['a','b',7]
]
プログラミングをするさいは、構造化データを意識して、元データを加工する習慣を付けましょう。(構造化データの詳しい解説は、データベースの考察のときにお届けしようと思っています)
ミニマムをとらえる
上記のような[元の配列]を、一度に[加工後の配列]に処理しようとするとハードルが高いです。課題は小さく切り分けて、やっつけていきましょう。
今回の場合だと、1レコードに対して、下記の処理を行います。
1. 配列の要素数が合わない場合は、要素数を合わせる
2. 配列の要素に配列が入っている場合は、中の配列の要素の数だけレコードを分割する
1レコードに対して処理を行った結果、新しい配列を生成します。
そして、レコードの数だけ反復処理を行います。
複数行のレコードを処理しなければならない場合は、「まず1行だとどう処理するのか?」のように、ミニマム(処理をする最小単位)でとらえるようにしましょう。
関数化のメリットと関数リテラル
何度も繰り返す処理は、関数化したほうがよいでしょう。
関数化することで、さまざまなメリットを得ることができます。
– コードが読みやすくなる
– 注意を払うべき処理に集中できる
– メンテナンス性が高くなる
– 他のスクリプトでも使える
特に、「注意を払うべき処理に集中できる」という意味では、2~3行の処理でも、あえて関数化して、関数名をつけた方が管理しやすいこともあります。
2~3行の処理は関数から外に出さずに、同じ関数内に 関数リテラル として関数化するパターンもあります。
/**
* Dateをプロパティストアにセットする関数
*
* @param {object} Dateオブジェクト
* @return none
*/
const setDateToPropertyStore = (date) => {
const dateFormat = Utilities.formatDate(date, "Asia/Tokyo", "yyyy/MM/dd");
const properties = PropertiesService.getScriptProperties();
properties.setProperties({"date":dateFormat});
}
たった数行の処理ですが、関数化することで、本来注意をはらうべき制御構文(例はif文)に集中でき、コードが読みやすくなります。
if(date){
setDateToPropertyStore(date);
}else{
console.log(`dateは空っぽです`);
}
Try…Catch構文
例外が発生したばあい、処理を続けるか、条件分岐ができます。
try {
//tryする処理
}
catch (エラー内容) {
//例外が発生したときの処理
}
[finally {
//例外が発生しようがしまいが最終的に行う処理
}]
実際のコードでは、レコードに[payments]プロパティがないばあい、例外が発生するため、try…catch構文で、paymentsArrayを作る処理を行っています。
let paymentsArray;
try {
paymentsArray = payments.map(payment => Object.values(payment)).flat();
} catch{
upperProperties.push(''); //配列の要素数を合わせる為
paymentsArray = [0, 0, 0, 0, 0];
}
スプレッド構文とmapメソッド
スプレッド構文
配列を結合するには「スプレッド構文」が便利です。配列名の前に …(ドット3つ) をつけると、配列の要素を展開する機能です。
「配列の要素を展開する」というのは、コード例を見ると理解が早いかもしれません。
const numbers = [1,2,3];
const strings = ['a','b','c'];
const combinedArray = [numbers, strings];
cconsole.log(combinedArray); //[[1,2,3],['a','b','c']]
const spreadArray = [...numbers, ...strings];
cconsole.log(combinedArray); //[1,2,3,'a','b','c']
mapメソッド
Arrayの組み込みオブジェクトであるmapメソッドは、反復メソッドとも呼ばれます。
配列の1つ1つの要素に対して処理を行った、「新しい配列」を生成するメソッドです。
以下のように、元になる配列名を複数形 にして、mapメソッドの引数に単数形 を使うと、コードがとても読みやすくなります。
const persons = ['Tom','Bob','John'];
const newPersons = persons.map(person => {
return `${person}様`;
}
);
console.log(newPersons); //['Tom様','Bob様','John様']
実際のコードでは、[details]プロパティの要素数だけ、新しいレコードを生成する役割を果たしています。
//detailsプロパティを基点に、fullArrayを整形
const fullArray = detailsArray.map(detail => {
return [...upperProperties, ...detail, ...paymentsArray];
}
);
前回の連載で、「このような関数を作成しました。」と一言で片付けてしまった関数はこちらです。
/**
* 1つの取引(収入/支出)オブジェクトを渡すと、全ての2次元配列を返す関数
*
* @param {object} 1つの取引(収入/支出)オブジェクト
* @return {object} 2次元配列
* //detailsプロパティを格納のマジックナンバーはfreeeでプロパティの増減仕様変更があると動かなくなる
*/
function getFullArray_(deal) {
//最上層のプロパティを格納
const upperProperties = Object.values(deal);
//detailsプロパティを格納
const details = deal['details'];
const detailsArray = details.map(detail => Object.values(detail));
//paymentプロパティを格納
const payments = deal['payments'];
//deal['payments']プロパティが無い場合、paymentsArrayを作る
let paymentsArray;
try {
paymentsArray = payments.map(payment => Object.values(payment)).flat();
} catch(e){
upperProperties.push(''); //配列の要素数を合わせる為
paymentsArray = [0, 0, 0, 0, 0];
Logger.log(e);
}
//detailsプロパティを基点に、fullArrayを整形
const fullArray = detailsArray.map(detail => {
return [...upperProperties, ...detail, ...paymentsArray];
}
);
return fullArray;
}
この関数は1レコードを渡すと、レコードを分析して、新しいレコード群を生成します。
まさに、この画像でお伝えした関数でした。
まとめ
以上で、「会計freeeで100件以上の取引を取得しよう」 の1回目として 「複雑な処理は関数を切り分けよう」 をお届けしました。
次回は、「レコードの開始値(オフセット値)を操作しよう」 をお届けします。
このシリーズの目次
[GAS][会計freee]100件以上の取引を取得しよう
1. [GAS][会計freee]複雑な処理は関数を切り分けよう
2. [GAS][会計freee]レコードの開始値(オフセット値)を操作しよう
3. [GAS][会計freee]スプレッドシートへ出力する関数