2015年06月14日

LINQ to Objectサンプル(11): 条件に一致する最後のインデックスを求める

  
要素そのものを取り出すのではなく、何番目の要素かを知りたい場合というのも良くありあす。
ここでは、配列、リストなどから、ある条件に一致する要素のインデックス(最後に見つかったもの)を求めるコードをお見せします。

■ループを使ったコード
var list = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, 8, -1, 0, 4 };
int index = -1;
for (int i = 0; i < list.Count; i++) {
    if (list[i] < 0)
        index = i;
}
Console.WriteLine(index);


■LINQを使ったコード
var list = new List<int> { 9, 7, -5, -4, 2, 5, 4, 0, -4, 8, -1, 0, 4 };
int index2 = list.Select((n, ix) => new { n, ix })
                 .Last(o => o.n < 0).ix;
Console.WriteLine(index2);


LINQを使ったコードでは、Selectメソッド(引数を2つ受け取るdelegateを引数に取るメソッド)を使い、要素の値とインデックスを対にした匿名クラスに変換しています。
その後、Lastメソッドで条件指定し、最後のインデックスを返しています。
短いコードなので、匿名クラスのメンバーにはプロパティ名を指定していませんが、 これが気になるって人は、匿名クラスを指定する際に、プロパティ名を指定すればよいでしょう。

例えば、

list.Select((n, ix) => new { Value=n, Index=ix })

といった感じでしょうか?

なお、Lastメソッドを使っているので、条件に一致する要素がなかった場合は、例外が発生します。



この記事へのコメント
汎用性を持たせるならともかく、今回のような単純な事例ではFindLastIndexのほうが直感的ではないでしょうか…
https://msdn.microsoft.com/ja-jp/library/xzs5503w(v=vs.110).aspx
Posted by 通りすがり at 2017年10月23日 21:15
コメントありがとうございます。Listと配列に限定すれば、おっしゃる通りです。拙著「実戦で役立つ C#プログラミングのイディオム/定石&パターン」でもそのように記述しています。
タイトルにもあるとおり、LINQ to Objectのサンプルなので、そこはご容赦を。
Posted by Gushwell at 2017年10月23日 21:27