2011-01-16 23 views
31

Tôi có một bộ sưu tập của các đối tượng bao gồm một biến TimeSpan:Sum của timespans trong C#

MyObject 
{ 
    TimeSpan TheDuration { get; set; } 
} 

Tôi muốn sử dụng LINQ để tổng hợp những khoảng thời gian. Tất nhiên, (từ r trong MyCollection chọn r.TheDuration) .Sum(); không hoạt động!

Tôi đang nghĩ đến việc thay đổi kiểu dữ liệu của TheDuration thành int và sau đó tổng hợp nó và chuyển đổi tổng thành một TimeSpan. Điều đó sẽ lộn xộn vì mỗi TheDuration trong bộ sưu tập của tôi được sử dụng như một khoảng thời gian ở một nơi khác.

Bất kỳ đề xuất nào về tóm tắt này?

Trả lời

74

Thật không may, không có quá tải Sum chấp nhận IEnumerable<TimeSpan>. Ngoài ra, không có cách nào để xác định ràng buộc chung dựa trên toán tử cho kiểu-tham số, vì vậy mặc dù TimeSpan là "nguyên bản" có thể tóm tắt, thực tế đó không thể được chọn dễ dàng bằng mã chung.

Một tùy chọn sẽ là, như bạn nói, tổng hợp một loại tích phân tương đương với khoảng thời gian thay thế, sau đó biến rằng tổng lại thành một lần nữa là TimeSpan. Các tài sản lý tưởng cho việc này là TimeSpan.Ticks, mà các chuyến đi khứ hồi chính xác. Nhưng không cần phải thay đổi kiểu thuộc tính trên lớp của bạn; bạn có thể chỉ dự án:

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

Ngoài ra, nếu bạn muốn dính vào khai thác + của TimeSpan để làm tổng hợp, bạn có thể sử dụng Aggregate điều hành:

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

Chỉ cần chỉnh sửa nhỏ. Nó phải là 'var totalSpan = new TimeSpan (myCollection.Sum (r => r.Ticks)); trong trường hợp nếu 'myCollection là 'Danh sách ()'. –

0

Bạn có thể sử dụng .Aggregate thay hơn .Sum và chuyển nó thành hàm sum sum-summing mà bạn viết, như sau:

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

@Ani: Vâng, vậy ...? – CesarGon

0

Đây là những gì tôi đã cố gắng và nó làm việc:

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; } 
} 
+0

điều này giống như giải pháp đầu tiên của @ Ani, chỉ với biểu thức truy vấn thay vì ký hiệu dấu chấm – BrokenGlass

+0

@Broken Glass phần nào .. theo thời gian tôi đăng câu trả lời này đã có 2 câu trả lời cho nó ..;) –

1

tôi đặt này trong một lớp học thêm một phương pháp mở rộng cho một bộ sưu tập của timespans:

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

Vì vậy, bây giờ, tôi có thể viết TotalDuration = (truy vấn LINQ của tôi trả về một tập hợp các khoảng thời gian) .TotalTime();

Thì đấy!

+1

bạn nên có * hỏi * cho một phương pháp mở rộng sau đó ;-) – BrokenGlass

+1

"Tôi muốn sử dụng LINQ để tổng hợp những lần đó." - bạn đã thay đổi tâm trí của bạn? –

+0

Tôi vẫn đang sử dụng LINQ: Tôi có thể truy vấn dữ liệu của mình rồi thêm .TotalTime(); ở cuối truy vấn và nó hoạt động. Tôi vẫn đang học khuôn khổ như bạn có thể thấy. – frenchie

28

này hoạt động tốt (mã dựa trên câu trả lời của 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); 
    } 
} 

Cách sử dụng:

Nếu Thời hạn là một danh sách các đối tượng với một tài sản Thời gian

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

+1 để có câu trả lời rõ ràng nhất. –

0

Tôi tin rằng đây là tiện ích LINQ sạch nhất:

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

Cách sử dụng là như nhau:

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

này hoạt động cho cả một bộ sưu tập, và một tài sản trong một bộ sưu tập;

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