配列(IEnumerable)の中から条件に当てはまる要素の添え字返却するLinq拡張


使わないようで、意外と使うので作っておくと、ちょっと便利?

    public static class LinqExtensions
    {
        public static int FirstIndex<T>(this IEnumerable<T> ie, Predicate<T> match)
        {
            return ie.Select((tData, index) => new { tData, index }).First(arg => match(arg.tData)).index;
        }

        public static int? FirstIndexOrNull<T>(this IEnumerable<T> ie, Predicate<T> match)
        {
            return ie.Select((tData, index) => new { tData, index }).FirstOrDefault(arg => match(arg.tData))?.index;
        }
    }

使い方

    class Program
    {
        static void Main(string[] args)
        {
            //               [ 0][ 1][ 2][ 3][ 4][ 5][ 7][ 8][ 9][10][11][12]
            var list = new[] { 1,  2,  3,  4,  5, 10, 20, 31, 32, 33, 34, 35};

            var index1 = list.FirstIndex(i => i == 31);
            Console.WriteLine("31が格納されているのは:" + index1 + "番目の要素でした。");  //出力:31が格納されているのは:7番目の要素でした。

            var index2 = list.Select(i => i * 2).FirstIndex(i => i > 15);
            Console.WriteLine("2倍した値で初めて15を超えるのは:" + index2 + "番目の要素でした。");//出力:2倍した値で初めて15を超えるのは:5番目の要素でした。

            //var index3 = data.FirstIndex(i => i == 0);    // LinqのFirst同様、存在しない場合は "ハンドルされていない例外: System.InvalidOperationException: シーケンスに、一致する要素は含まれてません" の例外発生

            var index4 = list.FirstIndexOrNull(i => i == 0);    //見つからない可能性がある場合はFirstOrDefaultのようにFirstOrNullを使用
            if (index4.HasValue)
            {
                Console.WriteLine("0が格納されているのは:" + index4 + "番目の要素でした。");
            }
            else
            {
                Console.WriteLine("0は見つかりませんでした。");
            }
        }
    }

もちろんIEnumerable<T>なので

            var strList = new[] {"abc", "def", "ghijkl", "mnoprstu", "vwxyz"};
            var index5 = strList.FirstIndex(s => s.Length > 5);
            Console.WriteLine("文字数が5文字を超えるのは:" + index5 + "番目の要素でした。");//出力:文字数が5文字を超えるのは:2番目の要素でした。

の様に、stringや自前のクラスでもウッドボール。

ところがどっこい

ここまで書いて、検証処理ちょっと走らせていたらListクラスには最初からFindIndexってメソッドがある事に気付きました。
なので、

            var strList = new[] {"abc", "def", "ghijkl", "mnoprstu", "vwxyz"};
            var index6 = strList.ToList().FindIndex(s => s.Length > 5);
            Console.WriteLine("文字数が5文字を超えるのは:" + index6 + "番目の要素でした。");//出力:文字数が5文字を超えるのは:2番目の要素でした。

の様にToList()を挟んであげれば、わざわざ自前で拡張クラス作る必要もないんですね・・・(ToListのコストがあるので優位性が無いわけではない…? 中で匿名クラスがnewされてるからむしろ不利か…。)

基礎ができてないって辛い。