LINQのAverage、Max、Min、Sumについて


はじめに

 LINQ、超便利ですよね!LINQを使わず書いた6、7行のコードが、LINQを使うことで1行で書くことができます。とても簡潔に、そして読みやすくなりますよね。もう自分はLINQなしでC#のコードを書くのは考えられません。

 この投稿ではAverage、Max、Min、Sumメソッドを紹介します。

Average、Max、Min、Sumについて

 この投稿では今後、IEnumelable<T>のことをT型のシーケンスと呼びます。またこの投稿で、数値型とはint(Int32)、long(Int64)、float(Single)、double(Double)、decimal(Decimal)を指します。

 Average、Max、Min、Sumそれぞれのメソッドでできることは、そのメソッドの名前が表す通りのものです。

  • Average:シーケンスの平均値を求める
  • Max:シーケンスの最大値を求める
  • Min:シーケンスの最小値を求める
  • Sum:シーケンスの合計値を求める

 Average、Max、Min、Sumは非常に多くのオーバーロードを持っています。それぞれ各数値型のシーケンスに対してのオーバーロードがあります。また、Nullable<long>やNullable<double>のように各数値型のnull許容型のシーケンスのオーバーロードを持っています。

 またMaxとMinに関しては、intなどの数値型やそのnull許容型だけでなく、任意の型のシーケンスで呼び出すことが可能です。もしその型がIComparable<T>かIComparableを実装している場合、このメソッドはその実装を使用して値を比較した最大値・最小値を得ることが可能です。

 オーバーロードの数が非常に多いので、それぞれを個別に説明することはしません。

数値型と(そのnull許容型)のシーケンスの平均値、最大値、最小値、合計値を計算するもの

例えば次のようなコードです。

例(1)
double average = new List<int> {0, 1, 2, 3 }.Average (); // 1.5

int? max = new List<int?> {1, 4, 1, null, 4, 2, null, 3, 5, 6}.Max (); // 6

long min = new long[] {2, 2, 3, 4}.Min (); // 2

float sum = new float[] {3.0F ,1.0F, 4.0F, 1.0F, 5.0F, 9.0F, 2.0F}.Sum (); // 25.0F

 Average、Max、Min、Sumは、上記のように数値型のシーケンスの平均値、最大値、最小値、合計値を求めます。

変換関数を使って、変換した結果を計算するもの

 次のようなRecordClassを使います。

RecordClass
public class RecordClass
{
    public int Score { get; set; }
}

 引数に変換関数(デリゲート)を渡すオーバーロードです。次のコードではFunc<Record,int>を渡しています。recordListの各要素のScoreプロパティに対する平均値、最大値、最小値、合計値を求めています。

例(2)
List<RecordClass> recordClassList = GetRecordClassList ();
double average = recordClassList.Average (record => record.Score);
int max = recordClassList.Max (record => record.Score);
int min = recordClassList.Min (record => record.Score);
int sum = recordClassList.Sum (record => record.Score);

数値型以外のもの(MinとMax)

 数値型以外にも、MinとMaxは任意の型に対して呼び出すことが可能です。もしその型がIComparable<T>かIComparableを実装している場合、このメソッドはその実装を使用して値を比較した最大値・最小値を得ることが可能です。

 例を示します。

例(3)
List<string> jvmLanguages = new List<string> {
    "Java", "Scala", "Groovy", "Clojure", "JRuby", "Jython", "Kotlin", "Xtend", "Ceylon"
};
Debug.Log (jvmLanguages.Max()); // Xtend
Debug.Log (jvmLanguages.Min()); // Ceylon


TimeSpan[] timeSpans = new TimeSpan[] {
    new TimeSpan(1, 0, 0), new TimeSpan(0, 1, 0), new TimeSpan(0, 0, 1)
};
Debug.Log (timeSpans.Max ()); // 01:00:00
Debug.Log (timeSpans.Min ()); // 00:00:01

シーケンスが空の場合の各メソッドの挙動

Average

  • Average<int>などは、要素が空の場合、例外がスローされます。
  • Average<Nullable<int>>などは、シーケンスの要素が空かnull値のみを含む場合、nullを返します。

Funcを引数にとる場合、

  • Func<T, int>などを引数に取るものは要素が空の場合例外が発生します。
  • Func<T, Nullable<int>>などを引数にとるものは、ソースシーケンスが空かnull値のみを含む場合はnullを返します。

MaxとMin

  • Max<TSource>はTSourceが参照型で、シーケンスが空かnull値のみを含む場合、nullを返します。

  • Max<TSource>はTSourceが値型で、シーケンスが空の場合、例外をスローします。

  • Max<int>などはシーケンスが空の場合、例外をスローします。

  • Max<Nullable<int>>などはシーケンスが空もしくはnull値のみを含む場合、nullを返します。

Minも同様です。

Sum

  • Sum<int>などは要素が含まれない場合は0を返します。
  • Sum<Nullable<int>>などは要素が含まれない場合0を返します。

※ Func系を引数にとるものも同じで0を返します。
※ Nullblle系の返り値型は、Nullable<int>型で、nullでなく0値を持っているもの。

MSDNへのリンク

 MSDNへのリンクを下記に示します。

まとめ

 LINQの中でも、Average、Max、Min、Sumは、理解しやすく、使いやすく、利用できそうな場面が多いですよね。

補足

 MinやMaxなどこの投稿でとりあげたLINQメソッドは、以前のUnityでは問題がありました。いくつかの条件を満たしてしまった場合、AOTコンパイラの不具合により、iOSなどAOTコンパイルが必要なプラットフォームで実行時例外が発生したのです。

 現在Unityでは、かつてAOTコンパイルが行われていたプラットフォームで、IL2CPPという技術が使われています。

 私が把握している限り、IL2CPPでは本投稿で紹介したメソッドは、問題なく使えているようです。そのため本投稿の記事タイトルを変更し、内容も各メソッドの紹介のみとしました。

 過去の内容を見たい場合は、編集履歴よりご覧ください。