web-dev-qa-db-ja.com

C#:Enumerable.Aggregateメソッドの使用方法

この切断されたPersonクラスがあるとしましょう:

class Person
{
    public int Age { get; set; }
    public string Country { get; set; }

    public int SOReputation { get; set; }
    public TimeSpan TimeSpentOnSO { get; set; }

    ...
}

次に、次のようにAgeCountryでグループ化できます。

    var groups = aListOfPeople.GroupBy(x => new { x.Country, x.Age });

次に、すべてのグループをレピュテーションの合計とともに次のように出力できます。

foreach(var g in groups)
    Console.WriteLine("{0}, {1}:{2}", 
        g.Key.Country, 
        g.Key.Age, 
        g.Sum(x => x.SOReputation));

私の質問は、どうすればTimeSpentOnSOプロパティの合計を取得できますか? Sumメソッドは、intなどにのみ使用されるため、この場合は機能しません。 Aggregateメソッドを使用できると思いましたが、使用方法が真剣にわかりません...さまざまな組み合わせですべての種類のプロパティと型を試していますが、コンパイラが認識しません。

foreach(var g in groups)
    Console.WriteLine("{0}, {1}:{2}", 
        g.Key.Country, 
        g.Key.Age, 
        g.Aggregate(  what goes here??  ));

Aggregateメソッドを完全に誤解しましたか?または何が起こっているのですか?代わりに使用する必要がある他の方法はありますか?または、Sums用に独自のTimeSpanバリアントを作成する必要がありますか?

そして、混乱に加えて、Personが匿名クラスである場合、たとえばSelectまたはGroupJoinステートメントの結果はどうなりますか?


最初にAggregateプロパティでSelectを実行すると、TimeSpanメソッドを機能させることができることがわかりました...しかし、そのような迷惑なことに気づきました...それでも私はこの方法をまったく理解していないと感じています...

foreach(var g in groups)
    Console.WriteLine("{0}, {1}:{2}", 
        g.Key.Country, 
        g.Key.Age, 
        g.Select(x => x.TimeSpentOnSO)
        g.Aggregate((sum, x) => sum + y));
19
Svish

クリスとダニエルズの答えの組み合わせは私のためにそれを解決しました。 TimeSpanを初期化する必要があり、間違った順序で処理を実行しました。解決策は次のとおりです。

foreach(var g in groups)
    Console.WriteLine("{0}, {1}:{2}", 
        g.Key.Country, 
        g.Key.Age, 
        g.Aggregate(TimeSpan.Zero, (sum, x) => sum + x.TimeSpentOnSO));

ありがとう!

そしてまた... D'oh!

2
Svish
List<TimeSpan> list = new List<TimeSpan>
    {
        new TimeSpan(1),
        new TimeSpan(2),
        new TimeSpan(3)
    };

TimeSpan total = list.Aggregate(TimeSpan.Zero, (sum, value) => sum.Add(value));

Debug.Assert(total.Ticks == 6);
24
g.Aggregate(TimeSpan.Zero, (i, p) => i + p.TimeSpentOnSO)

基本的に、Aggregateの最初の引数は初期化子であり、2番目の引数で渡される関数の「i」の最初の値として使用されます。リストを繰り返し処理し、毎回「i」にこれまでの合計が含まれます。

例えば:

List<int> nums = new List<int>{1,2,3,4,5};

nums.Aggregate(0, (x,y) => x + y); // sums up the numbers, starting with 0 => 15
nums.Aggregate(0, (x,y) => x * y); // multiplies the numbers, starting with 0 => 0, because anything multiplied by 0 is 0
nums.Aggregate(1, (x,y) => x * y); // multiplies the numbers, starting with 1 => 120
14
Chris Doggett

TimeSpanSumメソッドを書くことができます。

public static TimeSpan Sum(this IEnumerable<TimeSpan> times)
{
    return TimeSpan.FromTicks(times.Sum(t => t.Ticks));
}
public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source,
    Func<TSource, TimeSpan> selector)
{
    return TimeSpan.FromTicks(source.Sum(t => selector(t).Ticks));
}

または、 MiscUtil にはジェネリック対応のSumメソッドがあるため、SumTimeSpanで問題なく機能するはずです(TimeSpan+TimeSpan=>TimeSpan演算子定義)。

ただお願いします番号を教えてはいけません...それは私を怖がらせるでしょう...

1
Marc Gravell

TimeSpanのTotalプロパティの1つを合計することができます。たとえば、次のようにSOに費やされた合計時間数を表すことができます:

g.Sum(x => x.SOReputation.TotalHours)

これであなたが探している結果が得られると思いますが、必要なもの(時間、分、秒、日など)に応じて測定単位を入力する必要があることに注意してください。

0
Joseph