徳島ゲーム開発ごっこ 技術ブログ

ゲームを作るために役に立ったり立たなかったりする技術を学んでいきます!

【C#,LINQ】Sum~配列やリストの要素の合計を求めたいとき~

 C#のLINQの関数であるSum()の使い方についてです。
 配列やリストなどのシーケンス内の要素の合計値を求めることが出来ます。
f:id:urahimono:20180628140427p:plain


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

 先生「この袋の中には、赤い玉が4個、黒い玉が3個入っています。合計いくつですか?」
 足し算を学んだころが思い出されます。
 今となっては、数値を足していけばいいだけなので簡単ですが、数が多いと面倒くさいですね。

 特にプログラム上では膨大な数を扱うため、計算処理そのものはプログラムに任せても、実際に全てを足す処理を書くとなると、やっぱり面倒くさいです。
 こういう場合、LINQSum()を使うことで、コードを書く作業が軽減するかもしれません。

要素が数値型の場合

 Sum()の使い方は簡単です。
 int型やfloat型といった、数値を扱う配列やリストに対してSum()を呼ぶだけで、合計値を取得できます。

public static decimal Sum( this IEnumerable<decimal> source );
Enumerable.Sum メソッド (IEnumerable(Decimal)) (System.Linq)

public static double Sum( this IEnumerable<double> source );
Enumerable.Sum メソッド (IEnumerable(Double)) (System.Linq)

public static float Sum( this IEnumerable<float> source );
Enumerable.Sum メソッド (IEnumerable(Single)) (System.Linq)

public static int Sum( this IEnumerable<int> source );
Enumerable.Sum メソッド (IEnumerable(Int32)) (System.Linq)

public static long Sum( this IEnumerable<long> source );
Enumerable.Sum メソッド (IEnumerable(Int64)) (System.Linq)

Program.cs

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

public static class Program
{
    static void Main( string[] args )
    {
        // 数値データ
        int[]       intNumbers      = new int[]     { 1, 4, 3, 4 };
        float[]     floatNumbers    = new float[]   { 1.1f, 3.3f, 6.0f };

        int     intSum      = intNumbers.Sum();
        float   floatSum    = floatNumbers.Sum();

        // 結果発表
        System.Console.WriteLine( "intNumbers:{0}",   intNumbers.Text() );
        System.Console.WriteLine( "合計:{0}",         intSum );

        System.Console.WriteLine( "floatNumbers:{0}",   floatNumbers.Text() );
        System.Console.WriteLine( "合計:{0}",           floatSum );

        // 入力待ち用
        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

intNumbers:[1], [4], [3], [4],
合計:12
floatNumbers:[1.1], [3.3], [6],
合計:10.4

要素が数値型でNull許容型の場合

 Sum()Null許容型にも対応しています。
 Null許容型の要素の配列やリストに対して同じようにSum()を呼んであげるだけです。

public static decimal? Sum( this IEnumerable<decimal?> source );
Enumerable.Sum メソッド (IEnumerable(Nullable(Decimal))) (System.Linq)

public static double? Sum( this IEnumerable<double?> source );
Enumerable.Sum メソッド (IEnumerable(Nullable(Double))) (System.Linq)

public static float? Sum( this IEnumerable<float?> source );
Enumerable.Sum メソッド (IEnumerable(Nullable(Single))) (System.Linq)

public static int? Sum( this IEnumerable<int?> source );
Enumerable.Sum メソッド (IEnumerable(Nullable(Int32))) (System.Linq)

public static long? Sum( this IEnumerable<long?> source );
Enumerable.Sum メソッド (IEnumerable(Nullable(Int64))) (System.Linq)

Program.cs

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

public static class Program
{
    static void Main( string[] args )
    {
        // 数値データ
        int?[]      intNumbers      = new int?[]    { 1, null, 3, null };
        float?[]    floatNumbers    = new float?[]  { null, null, null };

        int?    intSum      = intNumbers.Sum();
        float?  floatSum    = floatNumbers.Sum();

        // 結果発表
        System.Console.WriteLine( "intNumbers:{0}",   intNumbers.Text() );
        System.Console.WriteLine( "合計:{0}",         intSum );

        System.Console.WriteLine( "floatNumbers:{0}",   floatNumbers.Text() );
        System.Console.WriteLine( "合計:{0}",           floatSum );

        // 入力待ち用
        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

intNumbers:[1], , [3], ,
合計:4
floatNumbers:, , [],
合計:0

 ちなみに全ての要素がNullだった場合は、0が返ってきました。

数値型以外の要素の場合

 ではそれ以外の型の合計値を求める場合はどうすればいいでしょうか。
 その場合は、Sum()の第二引数に合計値を求める数値を返す処理を記述することで使用できます。

public static decimal Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, decimal> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Decimal)) (System.Linq)

public static double Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, double> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Double)) (System.Linq)

public static float Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, float> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Single)) (System.Linq)

public static int Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, int> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Int32)) (System.Linq)

public static long Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, long> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Int64)) (System.Linq)

 以下の例ではラムダ式を用いて、自作クラス内の合計を求めたい数値プロパティを渡す処理を記述しています。

Program.cs

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

public static class Program
{
    private class Parameter
    {
        public string   Name    { get; set; }
        public int      Age     { get; set; }
        
        public override string ToString()
        {
            return string.Format( "Name:{0}, Age:{1}", Name, Age );
        }
    }

    static void Main( string[] args )
    {
        // 年齢と名前データ。
        Parameter[] parameters = new Parameter[]
        {
            new Parameter() { Age = 52, Name = "正一郎" },
            new Parameter() { Age = 28, Name = "清次郎" },
            new Parameter() { Age = 20, Name = "誠三郎" },
            new Parameter() { Age = 18, Name = "征史郎" },
        };

        int ageSum = parameters.Sum( value => value.Age );

        // 結果発表
        System.Console.WriteLine( "parameters:{0}", parameters.Text() );
        System.Console.WriteLine( "年齢の合計:{0}", ageSum );

        // 入力待ち用
        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

parameters:[Name:正一郎, Age:52], [Name:清次郎, Age:28], [Name:誠三郎, Age:20],
[Name:征史郎, Age:18],
年齢の合計:118

 この方式の場合も、Null許容型が対応しています。

public static decimal? Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, decimal?> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Nullable(Decimal))) (System.Linq)

public static double? Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, double?> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Nullable(Double))) (System.Linq)

public static float? Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, float?> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Nullable(Single))) (System.Linq)

public static int? Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, int?> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Nullable(Int32))) (System.Linq)

public static long? Sum<TSource>( this IEnumerable<TSource> source, Func<TSource, long?> selector );
Enumerable.Sum(TSource) メソッド (IEnumerable(TSource), Func(TSource, Nullable(Int64))) (System.Linq)

 こんな感じにSum()を七兆回使って、いろいろな合計値を出してみてください。

LINQのリンク

  • LINQ一覧
    www.urablog.xyz

  • Max
     シーケンス(配列やリスト)内の最大値の要素を求めたい!
    www.urablog.xyz

  • Min
     シーケンス(配列やリスト)内の最小値の要素を求めたい!
    www.urablog.xyz

  • Average
     シーケンス(配列やリスト)内の要素の平均値を求めたい!
    www.urablog.xyz