C#:如何使用 Enumerable.Aggregate 方法

发布于 2024-07-23 23:56:22 字数 1569 浏览 4 评论 0原文

假设我有这个被截断的 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??  ));

我是否完全误解了聚合方法? 或者发生了什么事? 我应该使用其他方法吗? 或者我是否必须为 TimeSpan 编写自己的 Sum 变体?

更糟糕的是,如果 Person 是一个匿名类,例如来自 SelectGroupJoin 语句的结果,该怎么办?


只是发现如果我先对 TimeSpan 属性执行 Select ,我就可以使 Aggregate 方法起作用......但我发现这种情况烦人的...仍然觉得我根本不理解这个方法...

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));

Lets say I have this amputated Person class:

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

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

    ...
}

I can then group on Age and Country like this:

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

Then I can output all the groups with their reputation totals like this:

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

My question is, how can I get a sum of the TimeSpentOnSO property? The Sum method won't work in this case since it is only for int and such. I thought I could use the Aggregate method, but just seriously can't figure out how to use it... I'm trying all kinds properties and types in various combinations but the compiler just won't recognize it.

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

Have I completely missunderstood the Aggregate method? Or what is going on? Is it some other method I should use instead? Or do I have to write my own Sum variant for TimeSpans?

And to add to the mess, what if Person is an anonymous class, a result from for example a Select or a GroupJoin statement?


Just figured out that I could make the Aggregate method work if I did a Select on the TimeSpan property first... but I find that kind of annoying... Still don't feel I understand this method at all...

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));

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

感情旳空白 2024-07-30 23:56:22
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);
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);
戴着白色围巾的女孩 2024-07-30 23:56:22
g.Aggregate(TimeSpan.Zero, (i, p) => i + p.TimeSpentOnSO)

基本上,Aggregate 的第一个参数是一个初始值设定项,它用作第二个参数中传递的函数中“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
g.Aggregate(TimeSpan.Zero, (i, p) => i + p.TimeSpentOnSO)

Basically, the first argument to Aggregate is an initializer, which is used as the first value of "i" in the function passed in the second argument. It'll iterate over the list, and each time, "i" will contain the total so far.

For example:

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
∝单色的世界 2024-07-30 23:56:22

您可以编写 TimeSpan Sum 方法...

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 方法,因此 Sum 应该可以在 TimeSpan 上正常工作(因为有定义的 TimeSpan+TimeSpan=>TimeSpan 运算符)。

只是不要告诉我号码...这会吓到我...

You could write TimeSpan Sum method...

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));
}

Alternatively, MiscUtil has generic-enabled Sum methods, so Sum should work on a TimeSpan just fine (since there is a TimeSpan+TimeSpan=>TimeSpan operator defined).

Just please don't tell me the number... it would scare me...

情泪▽动烟 2024-07-30 23:56:22

克里斯和丹尼尔斯的答案组合为我解决了这个问题。 我需要初始化 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));

谢谢!

还有... 天啊!

A combination of Chris and Daniels answers solved it for me. I needed to initialize the TimeSpan, and I did things in the wrong order. The solution is:

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));

Thanks!

And also... D'oh!

流年里的时光 2024-07-30 23:56:22

您可以对 TimeSpan 的 Total 属性之一求和。 例如,您可以像这样获得在 SO 上花费的时间的代表 TotalHours:

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

我相信这会给您带来您正在寻找的结果,但需要注意的是您必须根据您的情况设置度量单位需要(小时、分钟、秒、天等)

You could sum on one of the Total properties of the TimeSpan. For instance, you could get the represented TotalHours of time spent on SO like this:

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

I believe this would give you the result you're looking for, but with the caveat that you'd have to put the units of measure according to what you need (hours, minutes, second, days, etc.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文