はじめに
どうも、make屋のたにあんです。最近はmake Communityで回答に励んでいます。打率は1割です。対戦よろしくお願いします。
makeの配列操作に苦労するシチュエーションが多かったですが、今回紹介するScenarioを思いついてからは、だいぶ扱えるようになりました。このテンプレートを習得すれば、Scenario作成時のBundle操作で困ることは少なくなると思います。実際に複数のScenarioを作成する中で、このテンプレートを応用できるケースが多く、汎用性の高いものになっていると思います。
概要
- 配列からユニークな値を抽出し、それらに対して個別の処理を行うScenarioの構築方法を解説します。
- Router・Array Aggregator・Repeater・Text Aggregatorを組み合わせて、効率的な処理を実装します。
- Scenarioと各Moduleの設定方法や注意点について詳しく説明します。
Scenario
今回の説明で使用するScenarioです。

blueprint.jsonも添付していますので、お手元の環境で試してみてください。
blueprint.json
{ "name": "[Sample] Get unique value from multiple bundles", "flow": [ { "id": 1, "module": "json:ParseJSON", "version": 1, "parameters": { "type": "" }, "mapper": { "json": "[\n {\n \"Text\": \"My Name is Alice. I am a 28-year-old graphic designer. I was born and raised in London but currently live in Tokyo.\",\n \"Emali Address\": \"alice@example.com\",\n \"Name\":{\n \"First Name\": \"Alice\",\n \"Last Name\": \"Mercury\"\n }\n },\n {\n \"Text\": \"Since moving to Japan three years ago, I have been creating artwork that fuses traditional Japanese patterns with modern design. Recently, I held a collaborative exhibition with a famous kimono brand. On my days off, I enjoy taking photographs around the Asakusa district.\",\n \"Emali Address\": \"alice@example.com\",\n \"Name\":{\n \"First Name\": \"Alice\",\n \"Last Name\": \"Mercury\"\n }\n },\n {\n \"Text\": \"My Name is Bob. I am a 45-year-old robotics engineer. After working in Silicon Valley for 15 years, I started my own company\",\n \"Emali Address\": \"bob@example.com\",\n \"Name\":{\n \"First Name\": \"Bob\",\n \"Last Name\": \"May\"\n }\n },\n {\n \"Text\": \"Currently, I'm focusing on developing care robots for the elderly. I'm also a father of three children, and weekend camping trips with my family have become our tradition. Recently, I've become interested in the fusion of AI and music, and I'm secretly planning to create a robot orchestra.\",\n \"Emali Address\": \"bob@example.com\",\n \"Name\":{\n \"First Name\": \"Bob\",\n \"Last Name\": \"May\"\n }\n },\n {\n \"Text\": \"My name is Charlie Taylor. I am a 19-year-old university student. I'm majoring in Environmental Science in Vancouver, Canada.\",\n \"Emali Address\": \"charlie@example.com\",\n \"Name\":{\n \"First Name\": \"Charlie\",\n \"Last Name\": \"Taylor\"\n }\n },\n {\n \"Text\": \"I've been fascinated by marine life since childhood, and I'm particularly passionate about dolphin intelligence research. At university, I started an independent research group working on developing new ways to communicate with dolphins. On weekends, I work as a barista at a café to save money for my research.\",\n \"Emali Address\": \"charlie@example.com\",\n \"Name\":{\n \"First Name\": \"Charlie\",\n \"Last Name\": \"Taylor\"\n }\n },\n {\n \"Text\": \"My name is David Deacon. I am a 52-year-old veteran explorer. I've traveled everywhere from Antarctica to deserts around the world.\",\n \"Emali Address\": \"david@example.com\",\n \"Name\":{\n \"First Name\": \"David\",\n \"Last Name\": \"Deacon\"\n }\n },\n {\n \"Text\": \"I'm particularly passionate about investigating unidentified species, and recently discovered a new species of alpine plant in the Himalayas. While I usually give lectures on nature conservation, I've secretly started writing and am working on a fantasy novel based on my adventures.\",\n \"Emali Address\": \"david@example.com\",\n \"Name\":{\n \"First Name\": \"David\",\n \"Last Name\": \"Deacon\"\n }\n }\n]" }, "metadata": { "designer": { "x": 0, "y": 150 }, "restore": { "parameters": { "type": { "label": "Choose a data structure" } } }, "parameters": [ { "name": "type", "type": "udt", "label": "Data structure" } ], "expect": [ { "name": "json", "type": "text", "label": "JSON string", "required": true } ], "interface": [ { "name": "Text", "type": "text" }, { "name": "Emali Address", "type": "text" }, { "name": "Name", "spec": [ { "name": "First Name", "type": "text" }, { "name": "Last Name", "type": "text" } ], "type": "collection" } ] } }, { "id": 5, "module": "builtin:BasicRouter", "version": 1, "mapper": null, "metadata": { "designer": { "x": 300, "y": 150 } }, "routes": [ { "flow": [ { "id": 3, "module": "builtin:BasicAggregator", "version": 1, "parameters": { "feeder": 1 }, "mapper": { "Emali Address": "{{1.`Emali Address`}}" }, "metadata": { "designer": { "x": 600, "y": 0 }, "restore": { "extra": { "feeder": { "label": "JSON - Parse JSON [1]" }, "target": { "label": "Custom" } } }, "interface": [ { "name": "array", "spec": [ { "name": "Emali Address", "type": "text" } ], "type": "array", "label": "Array" } ] } }, { "id": 2, "module": "util:SetVariables", "version": 1, "parameters": {}, "mapper": { "scope": "roundtrip", "variables": [ { "name": "uniqueEmailAddress", "value": "{{deduplicate(3.array)}}" } ] }, "metadata": { "designer": { "x": 900, "y": 0 }, "restore": { "expect": { "scope": { "label": "One cycle" }, "variables": { "items": [ null ] } } }, "expect": [ { "name": "variables", "spec": [ { "name": "name", "type": "text", "label": "Variable name", "required": true }, { "name": "value", "type": "any", "label": "Variable value" } ], "type": "array", "label": "Variables" }, { "name": "scope", "type": "select", "label": "Variable lifetime", "required": true, "validate": { "enum": [ "roundtrip", "execution" ] } } ], "interface": [ { "name": "uniqueEmailAddress", "type": "any", "label": "uniqueEmailAddress" } ] } } ] }, { "flow": [ { "id": 6, "module": "builtin:BasicAggregator", "version": 1, "parameters": { "feeder": 1 }, "mapper": { "Name": "{{1.Name}}", "Text": "{{1.Text}}", "text": "{{1.text}}", "Emali Address": "{{1.`Emali Address`}}" }, "metadata": { "designer": { "x": 600, "y": 300 }, "restore": { "extra": { "feeder": { "label": "JSON - Parse JSON [1]" }, "target": { "label": "Custom" } } }, "interface": [ { "name": "array", "spec": [ { "name": "Name", "spec": [ { "name": "First Name", "type": "text" }, { "name": "Last Name", "type": "text" } ], "type": "collection" }, { "name": "Text", "type": "text" }, { "name": "Emali Address", "type": "text" } ], "type": "array", "label": "Array" } ] } }, { "id": 7, "module": "util:GetVariables", "version": 1, "parameters": {}, "mapper": { "variables": [ "uniqueEmailAddress" ] }, "metadata": { "designer": { "x": 900, "y": 300 }, "restore": { "expect": { "variables": { "items": [ null ] } } }, "expect": [ { "name": "variables", "spec": { "name": "value", "type": "text", "label": "Variable name", "required": true }, "type": "array", "label": "Variables" } ], "interface": [ { "name": "uniqueEmailAddress", "type": "any", "label": "uniqueEmailAddress" } ] } }, { "id": 11, "module": "builtin:BasicRepeater", "version": 1, "parameters": {}, "mapper": { "step": "1", "start": "1", "repeats": "{{length(7.uniqueEmailAddress)}}" }, "metadata": { "designer": { "x": 1200, "y": 300, "name": "Repeater (The number of unique email addresses)" }, "restore": {}, "expect": [ { "name": "start", "type": "number", "label": "Initial value", "required": true }, { "name": "repeats", "type": "number", "label": "Repeats", "required": true, "validate": { "max": 10000, "min": 0 } }, { "name": "step", "type": "number", "label": "Step", "required": true } ], "interface": [ { "name": "i", "type": "number" } ] } }, { "id": 9, "module": "builtin:BasicRepeater", "version": 1, "parameters": {}, "mapper": { "step": "1", "start": "1", "repeats": "{{length(6.array)}}" }, "metadata": { "designer": { "x": 1500, "y": 300, "name": "Repeater (The number of outputs)" }, "restore": {}, "expect": [ { "name": "start", "type": "number", "label": "Initial value", "required": true }, { "name": "repeats", "type": "number", "label": "Repeats", "required": true, "validate": { "max": 10000, "min": 0 } }, { "name": "step", "type": "number", "label": "Step", "required": true } ], "interface": [ { "name": "i", "type": "number" } ] } }, { "id": 10, "module": "util:TextAggregator", "version": 1, "parameters": { "feeder": 9, "rowSeparator": "" }, "filter": { "name": "Email Addresses", "conditions": [ [ { "a": "{{6.array[9.i].`Emali Address`}}", "b": "{{7.uniqueEmailAddress[11.i].`Emali Address`}}", "o": "text:equal" } ] ] }, "mapper": { "value": "Text: {{6.array[9.i].text}}\n{{newline}}" }, "metadata": { "designer": { "x": 1800, "y": 300 }, "restore": { "extra": { "feeder": { "label": "Repeater (The number of outputs) [9]" } }, "parameters": { "rowSeparator": { "label": "Empty" } } }, "parameters": [ { "name": "rowSeparator", "type": "select", "label": "Row separator", "validate": { "enum": [ "\n", "\t", "other" ] } } ], "expect": [ { "name": "value", "type": "text", "label": "Text" } ], "interface": [ { "help": "Text", "name": "text", "type": "text" } ] } }, { "id": 12, "module": "util:SetVariables", "version": 1, "parameters": {}, "mapper": { "scope": "roundtrip", "variables": [ { "name": "emailBody", "value": "{{10.text}}" } ] }, "metadata": { "designer": { "x": 2100, "y": 300 }, "restore": { "expect": { "scope": { "label": "One cycle" }, "variables": { "items": [ null ] } } }, "expect": [ { "name": "variables", "spec": [ { "name": "name", "type": "text", "label": "Variable name", "required": true }, { "name": "value", "type": "any", "label": "Variable value" } ], "type": "array", "label": "Variables" }, { "name": "scope", "type": "select", "label": "Variable lifetime", "required": true, "validate": { "enum": [ "roundtrip", "execution" ] } } ], "interface": [ { "name": "emailBody", "type": "any", "label": "emailBody" } ] } } ] } ] } ], "metadata": { "instant": false, "version": 1, "scenario": { "roundtrips": 1, "maxErrors": 3, "autoCommit": true, "autoCommitTriggerLast": true, "sequential": false, "slots": null, "confidential": false, "dataloss": false, "dlq": false, "freshVariables": false }, "designer": { "orphans": [] }, "zone": "us2.make.com", "notes": [] } }
Scenarioの概要説明です。
1stルート:JSONをArray Aggregatorで集約し、含まれているメールアドレスをユニーク化
2ndルート:ユニーク化したメールアドレスと一致する配列からText(JSONのKey)の値を抽出
ポイント
Route + Array Aggregatorでルート毎に必要な配列を作成
前述の通り、Routerで分けた各ルートでは以下のような設定を行っています。
1stルートのArray Aggregator → JSONからEmail Addressのみを配列に集約

2ndルートのArray Aggregator → JSON全体を配列へ集約

なお、今回のScenarioでは使用していませんが、RouterとAggregatorの間にFilterを設定することで、特定の条件に一致する値のみを取得することも可能です。
ユニーク化にはdeduplicate関数を
関数は意外と使い勝手が良いです。Moduleとビルトイン関数のどちらで処理をさせるかは経験に基づいて判断していますが、join
、length
、flatten
、deduplicate
といった配列関連の関数は頻繁に使用しています。
前項で説明したAggregatorでユニーク化したいKeyのみを取得し、deduplicate
関数で重複を削除してユニーク化しています。

Set multiple variables
の使用理由を補足すると、特定のルートで処理した値を別のルートで使用したい場合、Set multiple variables
で変数を作成し、Get multiple variables
でその変数を別ルートから呼び出して使用できます。

Textの抽出
2ndルートでは、元のJSONで特定のメールアドレスと一致する場合のみ、Textを抽出します。メールアドレス毎にTextがまとめられます。
元のJSONデータは以下画像のようにBundleが自動分割されるため、Array Aggregator
でまとめてから2ndルートを実行しています。

1つ目のRepeater
は、ユニーク化したメールアドレスの件数に応じて実行回数を調整するために使用しています。

2つ目のRepeaterは、元のJSONの長さ分だけ繰り返し実行するために配置しています。

その後、Filterを使用してユニーク化したメールアドレスと元のJSONデータが一致する場合のみ、Text Aggregatorが実行されるようにしています。


なお、RepeaterやArray Aggregatorに関してはBundleの実行順とも関係が深いので、以下の記事も参考にしていただけると幸いです。
参考:make つまずきポイント解説 – Bundle処理とData Storeの活用法
RepeaterとText Aggregatorのセット
RepeaterとText Aggregatorを組み合わせた場合、Repeaterの繰り返し回数が2回以上でも、消費されるOperationsは1回のみです。2つ目のRepeaterとText Aggregatorそれぞれの実行で1opsを消費します。ただ、今回のケースでは1つ目のRepeaterは以降のモジュールを繰り返し実行されるためご注意ください。

また、応用例としてRepeaterとText Aggregatorの間にRouterを挿入し、Text AggregatorでJSONを作成するといった力技的な方法もあります。機会があればブログで公開するかもしれません。
コラム:Community参加のすすめ
興味のない方は読み飛ばしていただいて構いません。
実は、今回の処理方法を思いついたのは、社内のScenario作成中ではなく、make Communityでの質問回答を試行錯誤する中で生まれたものです。
make Communityの質問は、大まかに以下のようにカテゴライズできます(1ヶ月ほどCommunityを見てきた私見です)。
- そもそもどこから手をつけていいか分からないという方々の質問
- makeに関係のないITの初歩的な知識に関する質問
- 外部サービスAPIの仕様に関する質問(API Referenceを見れば解決するような類)
- 値の型に関する質問
etc…
- makeの仕様に関する質問
- Bundleや配列の処理に関する質問
- Text ParserやAggregatorなどのmake固有のModuleに関する質問
etc…
1番と2番はさておき、3番の内容は様々なデータ構造に対する効果的なScenario作成の体験ができるため、makeの機能理解に有効です。自分でScenarioを作る場合、使用するサービスに偏りが出たり、自動化の対象を考えるところから始める必要がありますが、Communityの投稿を見ることで様々なケースを学べます。
ただし、Community上の質問の質(書き方や説明の仕方など)はかなり低いので、過度な期待は禁物です。また、全て英語なので、翻訳を活用しながら上手く付き合ってください。
用語解説
- Scenario:どのような処理を自動化するか定義されたもの。ZapierのZaps、Power Automateのフロー、WorkatoのRecipeに相当します。
- Module:Scenario内で利用できる操作そのもの。ZapierやPower Automate、Workatoのアクションに相当します。
- Bundle:make特有のデータ基本単位です。Scenarioで定義した処理がBundle毎に適用されます。他のiPaaSには恐らく相当するものがありません。
- make Community:makeのユーザーコミュニティ
- Operations:Moduleを実行できる回数です。makeの課金形態はOperationsに依存しています。
おわりに
今回は、makeにおける配列操作を行う第一歩として、テンプレートのように利用できるScenarioを紹介しました。このテンプレートを活用することで、Bundleの操作に対する理解も深まり、複雑な処理の実装もスムーズに進められるようになるのではないかと思っています。
よきmake Lifeを。それではまた。