[GAS]オブジェクトにフィルターをかけて抽出しよう 配列による反復と抽出 Day3

obj3EffectiveGoogleAppsScript

どうも。つじけ(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()までは行っていませんが、目的は達成できたと思います。

-Blog-ObjectFilter/myFunction3_3.gs at main · tsujike/-Blog-ObjectFilter
[オブジェクトにフィルターをかけて抽出しよう. Contribute to tsujike/-Blog-ObjectFilter development by creating an account on GitHub.

まとめ

以上で、「反復メソッドによる抽出」をお届けしました。

map()メソッド内部のネストがすこし深いので、状況に応じて、処理を切り分けてもいいかなと思います。

「状況に応じて」というのは、「では、プロパティを抽出するのは、どのような業務要件のばあいか」 ということを考えることになります。

「メンバーのaddressプロパティが不要」ということは、実際の業務では「社歴を判定したい」という要件かもしれません。

そのばあいは、「勤続年数」というクラスが浮かび上がってきます。

どうしても手続き型で書き続けると、コードが複雑になってきます。

特定の処理範囲を切り出すなど、「実際の業務をクラス化」 する方法は、いつか記事にしたいと思います。

GitHubリポジトリ

GitHub - tsujike/-Blog-ObjectFilter: [オブジェクトにフィルターをかけて抽出しよう
[オブジェクトにフィルターをかけて抽出しよう. Contribute to tsujike/-Blog-ObjectFilter development by creating an account on GitHub.

このシリーズの目次

タイトルとURLをコピーしました