Salesforce フローでMap変数を使いたい

この記事では、Apexを利用して、フローでMap変数を使う方法を紹介します。

はじめに

フロービルダーではテキスト変数・数式変数・レコード変数・テキストコレクション変数など、様々な変数を使うことができます。
しかしSet変数やMap変数など、いくつかの標準では備わっていない変数が存在します。
みなさんはフローを作成している時、「Map変数を使えたらな」という場面に遭遇したことはありませんか?
本日はApexを利用して、フローでMap変数を使う方法を紹介していこうと思います。
Mapとはキーと値の対応付けをして格納できるコレクションです
例えば、「1」というキーに対して、「Apple」を関連付けておいて「1」と呼べば、「Apple」を参照できるというイメージになります
※今回使用するMap変数はキーとバリューのデータ型がテキスト型(String型)であることを前提としています。
 他のデータ型で扱いたいという方はApexコードの変数型を変更してご利用ください。

Apexコードの紹介

※Apexテストコードはページの最下部にございます
public class MapForFlow {

    // 内部保持用のMap型変数
    private Map<String,String> innerMap = new Map<String,String>();
    
    //キーを一時的に保持
    private String tmpKey;
    
    //呼び出し時の引数
    public class Param {
            @InvocableVariable(required=false label='キー' description='キー値を指定してください')
            public String flowKey;
        
        @InvocableVariable(required=true label='マップ' description='マップ変数を指定してください')
            public MapForFlow mff;
        
        @InvocableVariable(required=true label='メソッド' description='get関数:get  remove関数:remove  clear関数:clear')
            public String flowOpe;
    }
    
    //Mapで使用するキーを受け取る変数
    @AuraEnabled
    public List<String> key {
                get {
            //キーを返す:keySet関数
                        return new List<String>(innerMap.keySet());
                } 
                set {
            //入力されたキーを変数に代入
                        this.tmpKey = value.get(value.size() - 1);
                }
        }
    
    //Mapで使用するバリューを受け取る変数
    @AuraEnabled
    public List<String> value {
                get {
            //バリューを返す:values関数
                        return new List<String>(innerMap.values());
                } 
                set {
            //入力されたバリューとキーをMap変数に代入
                        innerMap.put(this.tmpKey,value.get(value.size() - 1));
                        this.tmpKey = NULL;
                }
        }
    
    //フローからMap関数を呼び出すためのメソッド
    @InvocableMethod
    public static List<String> mapMethod(Param[] param){
        //Mapが空でなければ
        if(!param[0].mff.innerMap.isEmpty()){
                //get関数
                if(param[0].flowOpe == 'get'){
                    List<String> returnValue = new List<String>();
                    returnValue.add(param[0].mff.innerMap.get(param[0].flowKey));
                return returnValue;
                //remove関数
                }else if(param[0].flowOpe == 'remove'){
                param[0].mff.innerMap.remove(param[0].flowKey);
                //clear関数
                }else if(param[0].flowOpe == 'clear'){
                param[0].mff.innerMap.clear();
                }
        }
        return NULL;
    }
        
}
MapForFlow.apxc
一見難しそうに見えますが、中身はシンプルな構造になっています。
フローからキーを受け取るための変数とバリューを受け取るための変数を用意しており、その両方の変数が値を受け取ることで、内部保持用のMapにキーとバリューをペアとして代入していくような処理となっています。
フローから使用するメソッドとして以下の3つを用意しています。
No. メソッド 処理
1 getメソッド キーを受け取り、対となるバリューを返す
2 removeメソッド キーを受け取り、Mapから対になるバリューとキーを削除する
2 clearメソッド Mapのキーとバリューを全て削除する
上記とは別に処理部分を変更することで自由度の高い処理ができるようになりますが、今回は汎用性を重視するため単純な機能のみのApexクラスとなっています。

フローでの使用方法

1.Map変数の作成

Map変数定義

No. データ型 Apexクラス
1 Apex定義 MapForFlow
上記のようにデータ型を「Apex定義」、Apexクラスを「MapForFlow」として変数を作成します。

2.Map変数の初期化

Map変数初期化

No. 変数 演算子
1 Map変数 追加 キー
2 Map変数 追加 バリュー
作成したMap変数はKey、Vauleといった内部変数を持っています。
画像のようにキーとバリューを交互に代入していくことによってMap変数にキーとバリューのペアが設定されます。
ここではキーに名前、バリューに年齢を入れる割り当てを行っています。
「A」をキーとしてバリューを「22」、「23」と2回代入していますが、Map変数は同じキーがPUTされた場合、バリューは上書きされるため、Map変数の中身は現在以下のようになっています。
No. キー バリュー
1 A 23
2 B 25
3 C 26

3.メソッドの使用

メソッドの使用方法

それぞれメソッドなどを呼び出したい場合、⊕からアクションを選択し、Apexクラス「MapForFlow」を選択します。
画像の画面が現れるため、引数としての値を入力してください。

今回用意している3つのメソッドの引数パターンをテーブルに表しています。
No. マップ メソッド キー
1 Map変数 get キー
2 Map変数 remove キー
3 Map変数 clear
※getとremoveの際はキーが必ず必要ですが、clearの際は必要ありません

4.getメソッドの使用

getメソッド実行結果

No キー バリュー
1 A 23
キーを「A」としてgetメソッドを実行しました。
結果が正しく23と返ってきていることが確認できます。

5.removeメソッドの使用

removeメソッド実行結果

実行前
No. キー バリュー
1 A 23
2 B 25
3 C 26
実行後
No. キー バリュー
1 A 23
2 C 26
キーを「B」としてremoveメソッドを実行しました。
Map変数からキー「B」とバリュー「25」が削除されていることが確認できます。

6.clearメソッドの使用

clearメソッド実行結果

clearメソッドを実行しました。
Map変数から全てのキーと全てのバリューが削除されていることが確認できます。

7.おまけ(keySetメソッドとvaluesメソッド)

Map変数を利用する場面と言えば、キーセットでループして対となるバリューを取り出すというのがスタンダードだと思います。
上記を実施するために以下のメソッドを実行する必要があります。
No. メソッド 処理
1 keySetメソッド キーをすべてセットとして返す
※フローにはセット変数がないため今回はリストとして返します
2 valuesメソッド バリューをすべてリストとして返す
Apexコードをじっくり読まれた方であればお気づきかもしれませんが、今回のメソッドは紹介した3つのメソッドとは異なり、簡単に実行することができます。
その実行方法とは、以下の画像の通りMap変数のkeyやvalueをコンポーネントで指定するだけです。

これまで紹介したメソッドとMap変数を正しく扱うことができれば、フローの処理をより簡潔にまとめることができると思います。
実際の使用方法がしっくり来なかった方は、ぜひ開発環境で実際に動かしてみましょう!

まとめ

フローでのMap変数の使用はいかがでしたか?
今回は基本的な使用方法をご説明しましたが、ループコンポーネントと合わせて使ったり、Map変数のキーとバリューの型を変更したりと、工夫次第で様々な処理を簡潔にすることができます。
※予想外の挙動を起こす可能性もございますので本番環境で使う場合は、下記のテストクラス等を参考にしてしっかりテストを行ってください。

テストクラス

@IsTest
public class MapForFlowTest {
    
    //テストデータ作成
    static List<MapForFlow.Param> createParams(String key, String value, String methodName){
        MapForFlow mff = new MapForFlow();
        List<String> keyList = new List<String>();
        List<String> valueList = new List<String>();
        keyList.add(key);
        valueList.add(value);
        mff.key = keyList;
        mff.value = valueList;
        
        List<MapForFlow.Param> params = new List<MapForFlow.Param>();
        MapForFlow.Param param = new MapForFlow.Param();
        param.FlowKey = key;
        param.FlowOpe = methodName;
        param.mff = mff;
        params.add(param);
        
        return params;
    }
        
    //get関数テスト
    @IsTest
    public static void test01(){
        List<MapForFlow.Param> params = createParams('A','22','get');
        
        List<String> result = new List<String>();
        
        Test.startTest();
        result = MapForFlow.mapMethod(params);
        Test.stopTest();
        
        System.assertEquals(result[0],'22');
        
    }
    
    //remove関数テスト
    @IsTest
    public static void test02(){
        List<MapForFlow.Param> params = createParams('A','22','remove');
        
        List<String> result = new List<String>();
        
        Test.startTest();
        result = MapForFlow.mapMethod(params);
        Test.stopTest();
        
        System.assertEquals(result,NULL);
        
    }
    
    //clear関数テスト
    @IsTest
    public static void test03(){
        List<MapForFlow.Param> params = createParams('A','22','clear');
        
        List<String> result = new List<String>();
        
        Test.startTest();
        result = MapForFlow.mapMethod(params);
        Test.stopTest();
        
        System.assertEquals(result,NULL);
        
    }
    
    //keySet関数テスト
    @IsTest
    public static void test04(){
        List<MapForFlow.Param> params = createParams('A','22',NULL);
        
        Test.startTest();
        System.debug(params[0].mff.key);
        Test.stopTest();
        
        System.assert(params[0].mff.key.size() == 1);
        
    }
    
    //value関数テスト
    @IsTest
    public static void test05(){
        List<MapForFlow.Param> params = createParams('A','22',NULL);
        
        Test.startTest();
        System.debug(params[0].mff.value);
        Test.stopTest();
        
        System.assert(params[0].mff.value.size() == 1);
        
    }
    
    //キー未設定テスト
    @IsTest
    public static void test06(){
        MapForFlow mff = new MapForFlow();
        
        Test.startTest();
        System.debug(mff.key.isEmpty());
        Test.stopTest();
        
        System.assert(mff.key.isEmpty());
        
    }
    
    //バリュー未設定テスト
    @IsTest
    public static void test07(){
        MapForFlow mff = new MapForFlow();
        
        Test.startTest();
        System.debug(mff.value.isEmpty());
        Test.stopTest();
        
        System.assert(mff.value.isEmpty());
        
    }
    
    //メソッド無しテスト
    @IsTest
    public static void test08(){
        List<MapForFlow.Param> params = createParams('A','22','unknown');
        
        List<String> result = new List<String>();
        
        Test.startTest();
        result = MapForFlow.mapMethod(params);
        Test.stopTest();
        
        System.assertEquals(result,NULL);
        
    }
    
}
MapForFlowTest.apxc