【问题标题】:Sum of TimeSpans in C#C# 中的时间跨度总和
【发布时间】:2011-01-16 00:33:32
【问题描述】:

我有一个包含 TimeSpan 变量的对象集合:

MyObject
{ 
    TimeSpan TheDuration { get; set; }
}

我想使用 LINQ 来总结这些时间。 当然,(from r in MyCollection select r.TheDuration).Sum();没用!

我正在考虑将 TheDuration 的数据类型更改为 int,然后对其求和并将总和转换为 TimeSpan。这会很混乱,因为我收藏中的每个 TheDuration 都在其他地方用作时间跨度。

对这个总结有什么建议吗?

【问题讨论】:

  • 我在 Linq 查询中使用了这种方法: ... DelaySum = TimeSpan.FromTicks(x.Sum(y => y.DELAY_TS.Value.Ticks)) ... 似乎适合我的目的。希望这对那里的人也有帮助:-)。

标签: c# linq timespan


【解决方案1】:

不幸的是,没有接受IEnumerable<TimeSpan>Sum 的重载。此外,目前还没有为类型参数指定基于运算符的泛型约束的方法,因此即使 TimeSpan 是“本机”可求和的,泛型代码也无法轻松获取这一事实。

如您所说,一种选择是对等效于时间跨度的整数类型求和,然后将 that 和再次转换为TimeSpan。理想的属性是TimeSpan.Ticks,它可以准确地往返。但是根本没有必要改变你的类的属性类型;你可以project

var totalSpan = new TimeSpan(myCollection.Sum(r => r.TheDuration.Ticks));

或者,如果您想坚持使用 TimeSpan 的 + 运算符进行求和,您可以使用 Aggregate 运算符:

var totalSpan = myCollection.Aggregate
                (TimeSpan.Zero, 
                (sumSoFar, nextMyObject) => sumSoFar + nextMyObject.TheDuration);

【讨论】:

  • 只是一个小的修正。应该是var totalSpan = new TimeSpan(myCollection.Sum(r => r.Ticks)); in case if myCollection 是List<TimeSpan>()
【解决方案2】:

这很好用(代码基于 Ani 的回答)

public static class StatisticExtensions
{    
    public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> selector)
    {
        return source.Select(selector).Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2);
    }
}

用法:

如果 Periods 是具有 Duration 属性的对象列表

TimeSpan total = Periods.Sum(s => s.Duration)

【讨论】:

    【解决方案3】:

    我相信这是最干净的 LINQ 扩展:

    public static class LinqExtensions
    {
        public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> func)
        {
            return new TimeSpan(source.Sum(item => func(item).Ticks));
        }
    }
    

    用法同理:

    TimeSpan total = Periods.Sum(s => s.Duration)
    

    【讨论】:

    • 辉煌,辉煌辉煌...
    【解决方案4】:

    这是我尝试过的方法,它成功了:

    System.Collections.Generic.List<MyObject> collection = new List<MyObject>();
    MyObject mb = new MyObject();
    mb.TheDuration = new TimeSpan(100000);
    collection.Add(mb);
    mb.TheDuration = new TimeSpan(100000);
    collection.Add(mb);
    mb.TheDuration = new TimeSpan(100000);
    collection.Add(mb);
    var sum = (from r in collection select r.TheDuration.Ticks).Sum();
    Console.WriteLine( sum.ToString());
    //here we have new timespan that is sum of all time spans
    TimeSpan sumedup = new TimeSpan(sum);
    
    
    public class MyObject
    {
        public TimeSpan TheDuration { get; set; }
    }
    

    【讨论】:

    • 这与@Ani的第一个解决方案相同,只是使用查询表达式而不是点表示法
    • @Broken Glass 有点.. 到我发布这个答案时,已经有 2 个答案了.. ;) 伙计们在这里真的很快
    【解决方案5】:

    我把它放在一个类中,为时间跨度集合添加扩展方法:

    public static class Extensions:
    {
        public static TimeSpan TotalTime(this IEnumerable<TimeSpan> TheCollection)
        {
            int i = 0;
            int TotalSeconds = 0;
    
            var ArrayDuration = TheCollection.ToArray();
    
            for (i = 0; i < ArrayDuration.Length; i++)
            {
                TotalSeconds = (int)(ArrayDuration[i].TotalSeconds) + TotalSeconds;
            }
    
            return TimeSpan.FromSeconds(TotalSeconds);
        }
    }
    

    所以现在,我可以编写 TotalDuration = (我的 LINQ 查询返回时间跨度的集合).TotalTime();

    瞧!

    【讨论】:

    • 你应该询问扩展方法然后;-)
    • “我想用 linq 来计算这些时间。” - 你改变主意了?
    • 我还在使用 linq:我可以查询我的数据,然后添加 .TotalTime();在查询结束时,它可以工作。如您所见,我仍在学习框架。
    • @grantnz:原来我不再使用 TimeSpan。
    • 每个@grantnz 你应该重写这个扩展来使用Ani's Aggregate answer
    【解决方案6】:

    这适用于集合和集合中的属性;

    void Main()
    {
        var periods = new[] {
            new TimeSpan(0, 10, 0),
            new TimeSpan(0, 10, 0),
            new TimeSpan(0, 10, 0),
        };
        TimeSpan total = periods.Sum();
        TimeSpan total2 = periods.Sum(p => p);
    
        Debug.WriteLine(total);
        Debug.WriteLine(total2);
    
        // output: 00:30:00
        // output: 00:30:00
    }
    
    
    public static class LinqExtensions
    {
        public static TimeSpan Sum(this IEnumerable<TimeSpan> timeSpanCollection)
        {
            return timeSpanCollection.Sum(s => s);
        }
    
        public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> func)
        {
            return new TimeSpan(source.Sum(item => func(item).Ticks));
        }
    }
    

    【讨论】:

      【解决方案7】:

      您可以使用.Aggregate 而不是.Sum,并将您编写的时间跨度求和函数传递给它,如下所示:

          TimeSpan AddTimeSpans(TimeSpan a, TimeSpan b)
          {
              return a + b;
          }
      

      【讨论】:

        【解决方案8】:

        一旦您了解时间跨度无法求和并知道使用Ticks,在我看来,just 将 long 转换为时间跨度的扩展看起来更像 linq-嗯。我相信它可以让读者对操作有一个更具可读性的看法:

        var times = new[] { new TimeSpan(0, 10, 0), new TimeSpan(0, 20, 0), new TimeSpan(0, 30, 0) };
        
        times.Sum(p => p.Ticks)
             .ToTimeSpan();      // output: 01:00:00
             
        

        这是一个扩展:

        public static class LongExtensions
        {
            public static TimeSpan ToTimeSpan(this long ticks)
                => new TimeSpan(ticks);
        }
        

        【讨论】:

          【解决方案9】:

          我为 IEnumerable 创建了扩展方法

          public static class TimeSpanExtensions
          {
              public static TimeSpan Sum(this IEnumerable<TimeSpan> source)
              {
                  return source.Aggregate(TimeSpan.Zero, (subtotal, time) => subtotal.Add(time));
              }
          }
          

          感谢我可以使用:

           IEnumerable<TimeSpan> times = new List<TimeSpan>();
           TimeSpan totalTime = times.Sum();
          

          【讨论】:

          • 对于未来的读者:grantnz 的答案更健壮,因为它允许选择器 (x => x.TimeSpan)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-12-16
          • 1970-01-01
          • 1970-01-01
          • 2011-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多