2011-02-12 6 views
2

LINQ를 배우고 있으며 다음 코드와 동일한 LINQ 쿼리를 원합니다.C# LINQ TimeSpan 평균 일수

IEnumerable 목록에는 정렬 된 날짜 목록이 포함됩니다 : 오래된 항목부터 최신 항목까지.

아래 코드는 배열의 첫 번째 요소 날짜, 두 번째 요소 날짜의 첫 번째 요소 날짜, 세 번째 요소의 두 번째 요소 날짜 등을 빼서 TimeSpan을 생성합니다. 그런 다음 TimeSpan.Days은 코드의 다른 곳에서 평균됩니다.

LINQ 쿼리가 배열 생성을 필요로하지 않는다고 믿습니다. IEnumerable<DateTime> 구조를 데이터 소스로 사용할 수 있습니다.

IEnumerable<DateTime> list; // contains a sorted list of oldest to newest dates 

// build DateTime array 

DateTime[] datesArray = null; 
TimeSpan ts; 
List<int> daysList = new List<int>(); 

datesArray = (from dt in list 
       select dt).ToArray(); 

// loop through the array and derive the TimeSpan by subtracting the previous date, 
// contained in the previous array element, from the date in the current array element. 
// start the loop at element 1. 

for (int i = 1; i < list.Count(); i++) 
{ 
    ts = datesArray[i].Subtract(datesArray[i - 1]); // ts is a TimeSpan type 
    daysList.Add(ts.Days); 
    // add the TimeSpan.Days to a List<int> for averaging elsewhere. 
} 

내가 원하는 생각

스콧

+0

'(목록에서 DT에서하는 DT를 선택) .ToArray()'더 나은로 기록 된'list.ToArray()가' – Gabe

답변

4

, 감사 :

double averageDays = list 
        .Skip(1) 
        .Zip(list, (next, prev) => (double)next.Subtract(prev).Days) 
        .Average(); 

이이 손실 평균 있습니다 마십시오. 대신 TotalDays을 사용하지 않으시겠습니까?

편집 :

이 쉽게 연속 델타를 계산하게 시퀀스의 '한 연기'버전으로 순서를 오버레이하는 것입니다 작동 방법. 델타를 평균하여 결과를 산출하는 것입니다.

비교를 위해, 기존 코드의 '충실한'번역과 같습니다

double averageDays = Enumerable 
        .Range(1, list.Count - 1) 
        .Average(i => (double)list[i].Subtract(list[i - 1]).Days); 
+1

는'Zip' 여기에 조금 불필요한 것, 그것은 Aggregate''으로 행할 수 있어야한다. –

+1

...하지만 코드의 의도를 이해하는 것이 훨씬 쉽습니다. 손쉬운 '집계'는 분명하지 않은 추악한 코드를 만듭니다. – spender

+0

Linq Zip은 Framework 4.0입니다. Framework 3.5 이하 솔루션이 필요합니다. 평균보다 통계 분석을위한 개별 일 델타 목록 만 있으면됩니다. – scottL

1

이 기능 스타일을 강조에 스크롤 바의 땅으로 도망 피하기 위해 더 Lisp 다운 들여 쓰기 스타일에 배치되고, 페이지의 오른쪽.

list.Skip(1) 
    .Aggregate(new { Previous = list.FirstOrDefault(), 
        Sum = 0.0, 
        Count = 0 }, 
       (a, n) => new { Previous = n, 
           Sum = a.Sum + (n - a.Previous).Days, 
           Count = a.Count + 1 }, 
       a => a.Sum/a.Count) 
+0

의도가 첫 번째 항목을 건너 뛰는 것이라면 Aggregate의 시드에서도 .Skip (1)을 호출하면 안됩니까? – vcsjones

+0

'TotalDays'를 원한다고 생각합니다.'Sum'을'double'으로 어떻게 정의했는지 봅니다. – Gabe

+0

@vcsjones : 첫 번째 항목이 시드에 미리로드되므로 스킵됩니다. –

2

평균이 필요한 경우 목록의 모든 요소를 ​​확인할 필요가 없습니다. 첫 번째와 마지막 것만 필요합니다.

var period = list.Last() - list.First(); 
var averageDays = (double)period.TotalDays/(list.Count() - 1); 
+0

에서 계산을 계산합니다. OP는 'DeltaD.Days'의 평균을 계산합니다. 여기에 내재 된 반올림은이 방법과 다른 점을 의미합니다. – Ani

+0

Sergey 감사합니다. 내가 제공 한 정보를 바탕으로 접근 방식이 수학적으로 정확하고 쉽습니다. 그러나 다른 통계 분석을 위해 개별 일 델타가 필요했습니다. 모드 등. 원래 게시물에 제공하지 않은 정보입니다. – scottL