どうも。つじけ(tsujikenzo)です。このシリーズでは「オブジェクトにフィルターをかけて抽出しよう」をお届けします。全3回でお送りします。今日は最終回です。
この記事は、#Effective GoogleAppsScriptタグがついております。
前回のおさらい
前回は、「反復メソッドによる抽出」をお届けしました。
今回は、「配列による反復と抽出」をお届けします。
今日のアジェンダ
- 主キーによる抽出
- マジックナンバーと条件の配列化
- 配列による反復と抽出
- 完成コード
主キーによる抽出
前回は、プロパティの抽出(列の抽出)を行いました。
//オブジェクト化レコーズ
const members = [
{ id: "tg001", name: "Tsujike", address: "Hokkaido" },
{ id: "tg002", name: "Takahashi", address: "Fukuoka" },
{ id: "tg003", name: "Etau", address: "Tokyo" },
];
//必要なプロパティのみ抽出
const filteredProperties = members.map(member => {
const keys = Object.keys(member);
const values = Object.values(member);
const newObj = {};
keys.forEach((key, index) => {
if (key === "name" || key === "id") newObj[key] = values[index];
});
return newObj;
});
console.log(filteredProperties);
//[ { id: 'tg001', name: 'Tsujike' },
// { id: 'tg002', name: 'Takahashi' },
// { id: 'tg003', name: 'Etau' } ]
今回は、行(レコード)の抽出です。
行は、(A列などの)ユニークな「主キー」列で判定します。
配列から条件にあう要素を抽出する
forEach()メソッド同様に、配列の各要素を条件判定して、新しい配列を生成するのが、filter()メソッドです。
主キー順に、新しい配列を取得するためには、主キーで反復を回す必要があります。
const lists = [{id:"A",number:0}, {id:"B",number:1}, {id:"C",number:2},{id:"D",number:3}];
const primaryKeys = ["D", "B"];
const mainKey = "id";
const filteredNumbers = primaryKeys.map(pk => {
return lists.filter(list => list[mainKey] === pk)[0]; // 見つかるのは1件なので[0]
});
console.log(filteredNumbers); //[{id:"D",number:3},{id:"B",number:1}]
プロパティを抽出したオブジェクト化レコーズに、filter()メソッドをぶつけただけでは、レコーズの順番をキープできません。
//「idが"tg002"」のメンバーと、「idが"tg001"」のメンバーのみを順番に抽出したい
const filteredMembers = filteredProperties.filter(property => {
return property["id"] === "tg002" || property["id"] === "tg001";
});
console.log(filteredMembers);
// 結果は、オブジェクトレコーズの順番になってしまう。
// [ { id: 'tg001', name: 'Tsujike' }, { id: 'tg002', name: 'Takahashi' } ]
主キーで反復を回すためにも、マジックナンバーで指定していた主キーを、配列に格納しましょう。
マジックナンバーと条件の配列化
抽出したい条件が、ソースコード内にマジックナンバーとして記述されていることは、さまざまな問題を引きおこします。
条件式が無数に増えることは、読みづらいですし、できれば、条件となるプロパティや主キーはスプレッドシートで指定したいものです。
//読みづらい
if (key === "name" || key === "id"|| key === "property") ...
//配列で指定したい
const extraction = ["name","id","property"];
if (いい感じのextraction) ...
条件要素の配列化
まずは、ソースコード内の条件要素を配列に格納することから始めましょう。
//プロパティのフィルターに必要な配列を定義
const propertiesFilter = ["name", "id"];
//中略
//メンバーのフィルターに必要な配列を定義
const membersFilter = ["tg002", "tg001"];
//フィルターがけの対象となるプロパティを指定
const mainKey = "id";
スプレッドシートから取得した2次元配列も、1次元配列化して条件要素にできますね。(コードは割愛します)
配列による反復と抽出順の補償
配列を抽出条件にすると、抽出条件順を補償するコードも意識しやすくなります。
プロパティの抽出と、主キーによるレコードの抽出では、少し処理が異なります。
それぞれみていきましょう。
プロパティの抽出
プロパティの抽出は、map()メソッド内部の処理でした。
ここに、マジックナンバーが書かれているわけですから、マジックナンバーを抽出条件の配列にして、抽出順を壊さないように反復させます。
const keys = Object.keys(member);
const values = Object.values(member);
const newObj = {};
keys.forEach((key, index) => {
// if (key === "name" || key === "id") newObj[key] = values[index];
// この条件処理を、配列にして反復処理する
});
map()メソッド内部で、条件要素の配列[propertiesFilter]を、forEach()メソッドで反復すると、このようになります。
// プロパティのフィルターに必要な条件要素の配列
const propertiesFilter = ["name", "id"];
// membersを必要なプロパティのみでmapする
const filteredProperties = members.map(member => {
// 空のオブジェクト生成とレコードのプロパティを都度配列に格納
const keys = Object.keys(member);
const newObj = {};
// 条件要素の配列に対してループ処理
propertiesFilter.forEach(property => {
if (keys.includes(property)) newObj[property] = member[property];
});
// できあがったオブジェクトをmapにreturn
return newObj;
});
console.log(filteredProperties);
// [ { name: 'Tsujike', id: 'tg001' },
// { name: 'Takahashi', id: 'tg002' },
// { name: 'Etau', id: 'tg003' } ]
主キーによるレコードの抽出
レコードの抽出は、filter()メソッド内部の処理でした。
ここに、マジックナンバーが書かれているわけですから、filter()メソッド内の反復で処理させます。
const filteredMembers = filteredProperties.filter(property => {
// return property["id"] === "tg002" || property["id"] === "tg001";
// ここをいい感じに、抽出順も守りながら反復処理したい
});
おさらいですが、抽出順をまもるためには、条件要素で反復を回します。
抽出条件でいくと、2回しか反復処理を行わない気がしますが、反復メソッドの中で、全体を反復させるとよいでしょう。
//メンバーのフィルターに必要な配列を定義
const membersFilter = ["tg002", "tg001"];
//フィルターがけの対象となるプロパティを指定
const mainKey = "id";
// filteredPropertiesから必要なメンバー順で抽出する
const filteredMembers = membersFilter.map(memberId => {
return filteredProperties.filter(member => memberId === member[mainKey])[0]; // 見つかるのは1件なので[0];
});
console.log(filteredMembers);
// [ { name: 'Takahashi', id: 'tg002' }, { name: 'Tsujike', id: 'tg001' } ]
完成コード
以上で、全コードが完成しました。
スプレッドシートから値を取得したり、setValues()までは行っていませんが、目的は達成できたと思います。
まとめ
以上で、「反復メソッドによる抽出」をお届けしました。
map()メソッド内部のネストがすこし深いので、状況に応じて、処理を切り分けてもいいかなと思います。
「状況に応じて」というのは、「では、プロパティを抽出するのは、どのような業務要件のばあいか」 ということを考えることになります。
「メンバーのaddressプロパティが不要」ということは、実際の業務では「社歴を判定したい」という要件かもしれません。
そのばあいは、「勤続年数」というクラスが浮かび上がってきます。
どうしても手続き型で書き続けると、コードが複雑になってきます。
特定の処理範囲を切り出すなど、「実際の業務をクラス化」 する方法は、いつか記事にしたいと思います。