うら干物書き

ゲームを作りたい!

【C#,LINQ】Where~配列やリストを指定した条件でフィルタリングしたいとき~

 C#のLINQの関数であるWhere()の使い方についてです。
 配列やリストの指定した条件を満たす要素のみを取得することが出来ます。
f:id:urahimono:20180702002252p:plain


この記事には.NET Framework 4.6.1を使用しています。

指定した条件でフィルターをかける

 配列やリストの中で指定した条件を満たす要素を取得したい、そんなときはLINQWhere()が便利ですよ。

public static IEnumerable<TSource> Where<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate );
Enumerable.Where(TSource) メソッド (IEnumerable(TSource), Func(TSource, Boolean)) (System.Linq)

 Where()の引数に、条件をなる処理を記述することで、その条件に満たすものが返されます。
 以下の例ではラムダ式を用いて、条件文を記述しています。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        int[]       dataA   = new int[] { 0, 1, 2, 3, 4 };
        List<float> dataB   = new List<float>() { 1.5f, 1.3f, 3.2f };
        string[]    dataC   = new string[] { "正一郎", "清次郎", "誠三郎", "征史郎" };

        // 偶数のみの要素だけになるようにフィルタリング
        IEnumerable<int>    dataA_F = dataA.Where( value => value % 2 == 0 );
        // 2未満のみの要素だけになるようにフィルタリング
        IEnumerable<float>  dataB_F = dataB.Where( value => value < 2.0f );
        // 文字数が5文字未満の要素だけになるようにフィルタリング
        IEnumerable<string> dataC_F = dataC.Where( value => value.Length < 5 );

        System.Console.WriteLine( "dataA       :{0}", dataA.Text() );
        System.Console.WriteLine( "dataA Filter:{0}", dataA_F.Text() );
        System.Console.WriteLine( "dataB       :{0}", dataB.Text() );
        System.Console.WriteLine( "dataB Filter:{0}", dataB_F.Text() );
        System.Console.WriteLine( "dataC       :{0}", dataC.Text() );
        System.Console.WriteLine( "dataC Filter:{0}", dataC_F.Text() );

        // 入力待ち用
        System.Console.ReadKey();
    }

    /// <summary>
    /// 簡易的なシーケンスのテキスト取得処理
    /// </summary>
    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

} // class Program

dataA :[0], [1], [2], [3], [4],
dataA Filter:[0], [2], [4],
dataB :[1.5], [1.3], [3.2],
dataB Filter:[1.5], [1.3],
dataC :[正一郎], [清次郎], [誠三郎], [征史郎],
dataC Filter:[正一郎], [清次郎], [誠三郎], [征史郎],

条件に要素のインデックスを使いたい

 さて、条件となる処理を作る際に、各要素のインデックスも欲しいなという時もあるかもしれません。
 Where()ではインデックスも条件処理に使うこともできます。

public static IEnumerable<TSource> Where<TSource>( this IEnumerable<TSource> source, Func<TSource, int, bool> predicate );
Enumerable.Where(TSource) メソッド (IEnumerable(TSource), Func(TSource, Int32, Boolean)) (System.Linq)

 何か特別なことをするわけではなく、条件となる処理の第二引数にint型を用意すれば、そこに要素のインデックスが渡されます。
 以下の例では先ほどと同じように、ラムダ式を用いてインデックスも含めた条件文を記述しています。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        int[]       dataA   = new int[] { 0, 1, 2, 3, 4 };
        List<float> dataB   = new List<float>() { 1.5f, 1.3f, 3.2f };
        string[]    dataC   = new string[] { "正一郎", "清次郎", "誠三郎", "征史郎" };

        // 要素とインデックスが同じ要素だけになるようにフィルタリング
        IEnumerable<int>    dataA_F = dataA.Where( ( value, index ) => value == index );
        // 偶数のインデックスの要素だけになるようにフィルタリング
        IEnumerable<float>  dataB_F = dataB.Where( ( value, index ) => index % 2 == 0 );
        // 五番目の要素だけになるようにフィルタリング
        IEnumerable<string> dataC_F = dataC.Where( ( value, index ) => index == 5 );

        System.Console.WriteLine( "dataA       :{0}", dataA.Text() );
        System.Console.WriteLine( "dataA Filter:{0}", dataA_F.Text() );
        System.Console.WriteLine( "dataB       :{0}", dataB.Text() );
        System.Console.WriteLine( "dataB Filter:{0}", dataB_F.Text() );
        System.Console.WriteLine( "dataC       :{0}", dataC.Text() );
        System.Console.WriteLine( "dataC Filter:{0}", dataC_F.Text() );

        // 入力待ち用
        System.Console.ReadKey();
    }

    /// <summary>
    /// 簡易的なシーケンスのテキスト取得処理
    /// </summary>
    public static string Text( this IEnumerable i_source )
    {
        string text = string.Empty;
        foreach( var value in i_source )
        {
            text += string.Format( "[{0}], ", value );
        }
        return text;
    }

} // class Program

dataA :[0], [1], [2], [3], [4],
dataA Filter:[0], [1], [2], [3], [4],
dataB :[1.5], [1.3], [3.2],
dataB Filter:[1.5], [3.2],
dataC :[正一郎], [清次郎], [誠三郎], [征史郎],
dataC Filter:

 こんな風にWhere()を七兆回ほど使って、この世界の嫌なものをフィルタリングしてみてください。

LINQのリンク