ロゴ

自作のLINQカスタムメソッド集(随時更新)

作成日:2022年7月1日

目次

はじめに

LINQ、便利ですよね。
VB.net、C#でコレクション操作をするのにLINQ無しではやってられないほどです。

便利なLINQですが、
複雑な業務でシステムを作っていると、既存のLINQクエリメソッドだけでは賄いきれないケースもあります。

僕が業務の中で使用している自作のLINQカスタムメソッドをまとめてみました。

※LINQの拡張については「MoreLinq」「ExtraLinq」等が有名ですので、こちらも参考にしてみるとさらに面白いカスタムLINQが作れるかもですね。

実装方法

LINQのメソッドはIEnumerable<T>で定義されているので、IEnumerableの静的メソッドを定義すれば、
簡単に自作のLINQクエリメソッドが実装できます。
例として、Whereを自作する場合の実装方法を紹介します。

コレクションをforeachで回して、該当レコードだった場合、
イテレータ(yield)を使ってそのレコードを返します。
イテレータを使うことで遅延評価で実行可能となります。

例-自作Where

public static class EnumerableEx
{
   public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> f)
   {
       foreach (var item in source)
       {
           if (f(item)) yield return item;
       }
   }
}


NULL、Default値を除外する

基本的ですがよく使います。

public static IEnumerable<T> ExcludeNullOrDefault<T>(this IEnumerable<T> source)
{
    foreach (var item in source)
    {
        if (item == null || item.Equals(default(T))) continue;
        yield return item;
    }
}


検索結果をインデックス付きで返すWhere

順番に取り出してKeyValuePair等の辞書を作成します。

public static IEnumerable<KeyValuePair<int,T>> WhereWithIndex<T>(this IEnumerable<T> source, Func<T, bool> f)
{
    int index = -1;
    foreach (var item in source)
    {
        checked { index++; }//indexのオーバーフローを補足
        if (f(item)) yield return new KeyValuePair<int, T>(index, item);
    }
}


指定のフィールドの値が最も高い(低い)レコードを複数件取得

一番高いレコードを探す時点ですべての要素にアクセスするので、ここでは遅延評価に対応する必要はありません。

public static IEnumerable<TSource> MaxBy<TKey,TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
            where TKey:IComparable<TKey>,IEquatable<TKey>
{
    //最大値を取得
    var max = source.Max(keySelector);
    //最大値のレコードを全取得
    return source.Where(x=>keySelector(x).Equals(max));
}



特定基準日から数えて一年ごとにGroupBy

LINQの自作というより、GroupByの処理を共通化するためのメソッドです。
timestampを持っているクラスのコレクションを年度ごとでまとめることが可能です。
業務システムでは帳票データを年度ごとで集計するようなケースが多いので載せました。

 public static IEnumerable<IGrouping<int,T>> YearGroupBy<T>(this IEnumerable<T> source, Func<T, DateTime> keyselector,(int month,int day) reference)
 {
      return source.GroupBy(x =>
      {
          var date = keyselector(x);
     //基準月日を超得ていない場合、年度はdate-1になる。
          if (date.Day >= reference.day && date.Month >= reference.month)
          {
              return date.Year;
          }
          else
          {
              return date.Year - 1;
          }
      });
}


同一プロパティ名の値をコピーした別クラスのコレクションを取得

AutoMaapperのIEnumerable版。
例外処理はだいぶ省略してます。

public static IEnumerable<TTarget> AutoMap<TSource,TTarget>(this IEnumerable<TSource> source)
                where TTarget:new() where TSource:class
{
      foreach (var item in source)
      {
          var targetObj = new TTarget();
          foreach (var prop in typeof(TSource).GetProperties())
          {
              var targetProp =typeof(TTarget).GetProperty(prop.Name);
              //プロパティが存在しないor型が一致しない場合は処理をスキップ
              if (targetProp == null || targetProp.GetType()!=prop.GetType()) continue;
              targetProp.SetValue(targetObj, prop.GetValue(item));
          }
          yield return targetObj;
      }
}


まとめ

随時更新中。

関連記事

記事画像

2022年7月1日

Yakan

田舎育ちのアナログ人間ですが、システム作ってます。
趣味の制作はWebアプリ中心です。
仕事は業務系なのでC#メインで書いてます。

Yakan

田舎育ちのアナログ人間ですが、システム作ってます。
趣味の制作はWebアプリ中心です。
仕事は業務系なのでC#メインで書いてます。

© 2022 - 2023 Yakan.