ども、Norimakiです。
今回は無名メソッドでのソートについてです。
毎回、ハマるので記事にしてみました。
通常はTList<T>でデータを格納しておいて、
必要に応じてソートすると。
そういう状況を想定しています。
基本的にソースはこんな感じ。
1 2 3 4 5 6 |
Rt.Sort(TComparer<integer>.Construct( function(const Item1,Item2:integer):Integer begin end) ); |
RtはTList<integer>です。
ちなみに、RtがTList<TMyRecord>なら、
1 2 3 4 5 6 |
Rt.Sort(TComparer<TMyRecord>.Construct( function(const Item1,Item2:TMyRecord):Integer begin end) ); |
になります。
で、結論から言うと無名メソッドの引数、Item1・Item2は、
Rtに格納されている値です。
Rtのインデックスではありません。
まぁ普通は間違わないんでしょうけど、
僕はいつも勘違いしてしまうわけですよ。
格納している値がIntegerですし。
なので、比較する際にも、Item1・Item2が
Rtのインデックスと勘違いして、
Result:=Rt[Item1]-Rt[Item2];
みたいに書いちゃうわけです。でも実際は、
Result:=Item1-Item2;
が正解。
ここでは、TListがIntegerを格納しているから勘違いするわけで、
レコードとかを格納していると、間違わないんですけどね。
まぁ、言い訳です。
さて、
僕が間違える原因のもう一つとして挙げられるのが、僕は普段、
インデックスをリスト化している。ということがあります。
どういうことかというと、
まず、必要なデータをレコード型で定義しておいて、
それをTList<T>に格納すると。
そのデータをフィルタリングしたりソートしたりする際に、
実体をソートするのではなく別途インデックスのリストを用意して、
そちらを操作するということです。
たとえば、データの実体を格納するのに、
TList<TNYxxxxx>
というTListを用意したとします。
レコード(TNYxxxxx)の内容としては、名前(Name)と年齢(age)の
2つのデータを保持するとします。
そのリストはこんな感じとします。
(佐藤一郎,15歳)
(斎藤次郎,20歳)
(工藤三郎,37歳)
(灰原史郎,25歳)
(野比五郎,41歳)
(剛田六郎,31歳)
(毛利七郎,75歳)
(武田八郎,74歳)
(細川九郎,55歳)
(加藤十朗,34歳)
で、インデックスをリスト化するとは、別途リスト(TList<integer>)を用意して
フィルタリングをする際に上記データをインデックスで保持するということです。
実際にデータが必要になった場合は、まずリスト(TList<integer>)からインデックスを取得して、
その後実体(TList<TNYxxxxx>)をインデックスで参照すると。そういう方法をとります。
分かりにくいので、具体例を。
上記のデータは上からインデックスを付けると
(佐藤一郎,15歳)がインデックス0(TList<TNYxxxxx>[0])。
(斎藤次郎,20歳)がインデックス1(TList<TNYxxxxx>[1])。
以下同様に、
(加藤十朗,34歳)がインデックス9(TList<TNYxxxxx>[9])。
ということになります。
で、例として、「田」という文字を名前に含む人をフィルタリングするとします。
すると、
(剛田六郎,31歳)...インデックス5(TList<TNYxxxxx>[5])。
(武田八郎,74歳)...インデックス7(TList<TNYxxxxx>[7])。
が、フィルタリングした結果です。
なので、インデックスのリストはこんな感じになります。(TList<integer>)
(もちろん、TList<integer>の数(count)は2)
5 .... インデックス0(TList<integer>[0])
7 .... インデックス1(TList<integer>[1])
さて、ここからが本題。年齢をもとにソートをします。
当然、ソート対象はインデックスのリスト(TList<integer>)です。
まずは、正解から。
1 2 3 4 5 6 7 8 9 10 |
FIndexList.Sort(TComparer<integer>.Construct( function(const Item1,Item2:integer):Integer var Dt1,Dt2:TNYxxxxx; begin Dt1:=FBodyList[Item1]; Dt2:=FBodyList[Item2]; Result:=Dt1.Age-Dt2.Age; end) ); |
こんな感じになるでしょうか。
僕がよくやる間違いは、
1 2 |
Dt1:=FBodyList[Item1]; Dt2:=FBodyList[Item2]; |
を
1 2 |
Dt1:=FBodyList[FIndexList[Item1]]; Dt2:=FBodyList[FIndexList[Item2]]; |
とやってしまうことです。
FBodyListが実体のリスト、TList<TNYxxxxx>で
FIndexListがインデックスのリスト、TList<integer>になります。
一番先にも書いた通り、
無名メソッドでの引数、Item1・Item2はTListに格納されている値です。
つまり、
Item1 は FIndexList[Index1] であり、
Item2 は FIndexList[Index2] であるということ。
(Index1・Index2はFIndexListにおけるItem1,Item2のインデックス)
上記のサンプルで言うと、Item1、Item2は5とか7であり、
0や1ではないということです。
ここら辺をいつも勘違いするので記事にしておきました。
まぁ、備忘録ということで。
※表記上、TList<integer>[0]みたいに変な記述がありますが、
通常はXXX:=TList<integer>.Create;などとインスタンスを生成して
XXX[0]というように書くのが正解。
TList<integer>[0]の方が区別しやすいかなと思って
こっちの書き方にしました。
ではでは。
Norimakiでした。