top of page

ApexのList.sort()に限界を感じたら:Comparableインターフェースで実現するカスタムソート

  • hinoue2826
  • 10月20日
  • 読了時間: 6分
ree


   目次




   はじめに


・リスト型のコレクションのソート時に降順でソートしたい。

・レコード作成時に特定の項目の値を基準に連番を振りたい。

という場面で、困ったことはありませんか?


Apexでは時々List型のクラスに対して並び替え(ソート)を行いたい場合がありますが、List.sort()メソッドには昇順、降順やsObjectの場合にどの項目を基準にソートする等の設定が行えません。


この記事ではComparableインターフェースを活用して昇順、降順、ソート基準項目などを柔軟に設定できるカスタムソートの実装方法を紹介します。


💡Apexとは

Salesforce Platform上で実行されるプログラミング言語です。

標準機能やフローなどで実現できない複雑なロジックの実装やカスタムアプリケーションを構築する際に利用されます。



   ApexにおけるListソート


List型のクラスに対してはList.sort()メソッドでソートを行うことができますが、この方法では常に昇順でソートが行われ、ソート基準となる項目の指定は行えず、Listが格納するsObjectが持つ項目を対象に以下の順番で評価が行われます。


  1. sObject型の表示ラベル(sObjectどうしの比較)

  2. Name項目

  3. Id項目とName項目を除き、アルファベット順で最初の標準項目

  4. アルファベット順で最初のカスタム項目


※オブジェクトのメタデータ基準ではなく、あくまでListが格納するsObjectが保持する情報をもとに行われます。

例えばSOQLでName項目を取得しなければ、2のName項目の比較はスキップされます。



そのため、

「最終更新日が新しい順にソートしたい」

「商談の金額が大きい順にソートしたい」というように、

ソート基準の項目を指定したり、降順でソートする場合には、Comparableインターフェースを利用したカスタムソートメソッドを定義する必要があります。



アップビルダーが300名以上在籍!

テラスカイ・テクノロジーズのエンジニア派遣の資料ダウンロードはこちら



   おさらい:インターフェースとは


Comparableインターフェースの詳細に入る前に、インターフェースについておさらいしましょう。


インターフェースとはメソッドが定義されていないクラスのようなもので、インターフェースを継承して利用するためには、継承するクラスでそのインターフェースが定義するメソッドをすべて記載する必要があります。


例えば非同期処理で利用されるBatchableインターフェースを挙げると、Batchableインターフェースにはstart、execute、finishが定義されているため、Batchableインターフェースを継承するバッチクラスではstart、execute、finishメソッドを実装してからデプロイする必要があります。



   Comparableインターフェースを使ったカスタムソートの例


ComparableインターフェースはObjectを引数に持ち、Integerを返すcompareToメソッドを実装する必要があります。


Comparableインターフェース継承クラスのリストに対してソートを行うとcompareToメソッドが実行され、カスタムソートが行われるという仕組みになっています。


実際にComparableインターフェースを継承する商談のカスタムソートの例を見てみましょう。

public with sharing class OpportunityAmountSort implements Comparable{
	//商談を比較するのでプロパティの商談オブジェクトを持つ
    public Opportunity opp;
		
	//コンストラクター インスタンス変数の商談に引数の商談を割り当てる
    public OpportunityAmountSort(Opportunity argumentOpp){
        this.opp = argumentOpp;
    }
    
    public Integer compareTo(Object compareTo){
        OpportunityAmountSort compareToOpp = (OpportunityAmountSort)compareTo;

        if(this.opp.Amount == null){
            return 1; //thisの金額がnullだったらリストの後ろに配置する
        }

        Decimal argumentAmount = this.opp.Amount;
        Decimal compareToAmount = compareToOpp.opp.Amount == null ? 0 : compareToOpp.opp.Amount;        //nullの場合は0       

        if(argumentAmount > compareToAmount){
            return -1;
        }else if(argumentAmount < compareToAmount){
            return 1;
        }else{
            return 0;
        }
    }
}

compareToメソッドの内容は、比較対象となるもう一つのオブジェクトを引き受け、その対象とインスタンスの金額を比較して


・インスタンスの方が大きければList内のインデックスを-1(List内の位置を手前に移動させる)


・比較対象の方が大きければ1(List内の位置を後ろに移動させる)、この2つに該当しなければ0(順序を維持する)を返す


となっています。


Comparableインターフェースクラスの実装で注意するポイントとしては以下の点があります。


  1. 必ずcompareToメソッドを実装する

  2. 比較前に比較する項目がnullの場合の処理を行う


カスタムソートクラスの実装が完了したので、次に実際にカスタムソートを行う流れを見てみましょう。



   カスタムソートの実行


カスタムソートを実行するにはsObjectのリストを基にComparableインターフェース継承クラスのリストを作成し、それに対してList.sort()を実行する必要があります。


次のコードは金額がランダムまたはnullの商談を10件作成し、上のコードで定義したComparableインターフェース継承クラス(OpportunityAmoutSort)を生成してそのリストに対してソートを行っています。


//商談リスト
List<Opportunity> OppList = new List<Opportunity>();
for (Integer i = 9; i >= 0; i--) {

    Opportunity opp = new Opportunity();
  //ソート前は9からデクリメント
    opp.Name = 'カスタムソート商談' + String.valueOf(i);
    opp.StageName = '受注';
    opp.CloseDate = Date.today();
    //iが5の倍数でなければランダムな金額設定、5の倍数であればnull
    if(Math.mod(i,5) !=0 ){
        opp.Amount = Math.floor(Math.random() * 10000); //金額ランダム
    }
    //リストに追加
    OppList.add(opp);
}

 List<OpportunityAmoutSort> OAS = new List<OpportunityAmoutSort>(); 
//Comparableインターフェース継承クラスに追加 
for (Opportunity Opp : OppList) {
 OAS.add(new OpportunityAmoutSort(Opp));
 } 
//ソート実行 
OppList.sort();

では、通常のソートと定義したカスタムソートの結果を比較してみましょう。

//商談リスト
List<Opportunity> OppList = new List<Opportunity>();
for (Integer i = 9; i >= 0; i--) {

    Opportunity opp = new Opportunity();
  //ソート前は9からデクリメント
    opp.Name = 'カスタムソート商談' + String.valueOf(i);
    opp.StageName = '受注';
    opp.CloseDate = Date.today();
    //iが5の倍数でなければランダムな金額設定、5の倍数であればnull
    if(Math.mod(i,5) !=0 ){
        opp.Amount = Math.floor(Math.random() * 10000); //金額ランダム
    }
    //リストに追加
    OppList.add(opp);
}

 List<OpportunityAmountSort> OAS = new List<OpportunityAmountSort>(); 
//Comparableインターフェース継承クラスに追加 
for (Opportunity Opp : OppList) { 
    OAS.add(new OpportunityAmountSort(Opp)); 
} 
System.debug('---通常のソート---'); 
//ソート実行 
OppList.sort(); 
//結果出力 
for (Opportunity Opp : OppList) {
    System.debug(Opp); 
}
 System.debug('---カスタムソート---'); 
//ソート実行 
OAS.sort(); 
for (OpportunityAmountSort Opp : OAS) { 
//結果出力 
    System.debug(Opp); 
}

この結果が以下のスクリーンショットです。


ree

比較してみると通常のソートでは基本の基準に従ってName項目がアルファベット順で並んでいるのに対し、カスタムソートでは金額項目を基準に降順でソートされていることが分かりますね。



   まとめ


いかがだったでしょうか。


カスタムソートを利用すれば自動的に連番が振られるようなオブジェクトに対し、同じトランザクション内で金額順に連番を振ったり、compareToの評価で数式項目と同じ計算を行うことでinsert前に数式項目の値を計算してその順番通りに連番を振るなどの状況にも対応できます。


ぜひ使ってみてください!




アップビルダーが300名以上在籍!

テラスカイ・テクノロジーズのエンジニア派遣の資料ダウンロードはこちら



ree

bottom of page