С#: Как использовать метод 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; }
...
}
Затем я могу группировать по Age
и Country
следующим образом:
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? Или что происходит? Это какой-то другой метод, который я должен использовать вместо этого? Или мне нужно написать собственный вариант Sum
для TimeSpan
s?
И чтобы добавить к беспорядку, что, если 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));
Ответы
Ответ 1
Комбинация ответов Криса и Дэниелса решила его для меня. Мне нужно было инициализировать 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
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);
Ответ 3
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
Ответ 4
Вы можете написать метод 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
).
Просто пожалуйста не говорите мне номер... это меня пугает...
Ответ 5
Вы могли бы суммировать одно из свойств Total TimeSpan. Например, вы можете получить представленное TotalHours время, затраченное на SO следующим образом:
g.Sum(x => x.SOReputation.TotalHours)
Я считаю, что это даст вам результат, который вы ищете, но с оговоркой, что вам нужно будет поместить единицы измерения в соответствии с тем, что вам нужно (часы, минуты, секунды, дни и т.д.)