Using LINQ to do some calculations on the current and next object

Is there an elegant solution for viewing an ordered list to perform some calculations on the current and next object? For LINQ, you need to more intelligently do the following:

public static List<double> GetHoursBetweenDates(List<DateTime> aDates) { List<double> lst = new List<double>(); var olst = aDates.OrderByDescending(d => d).ToList(); for (int i = 0; i < olst.Count - 1; i++) { lst.Add(olst[i].Subtract(olst[i+1]).TotalHours); } return lst; } 
+7
c # datetime linq timespan
source share
4 answers

The easiest way to compare each subsequent item in the list is something like this:

 var sorted = aDates.OrderByDescending(d => d); var results = sorted.Zip(sorted.Skip(1), (a, b) => a.Subtract(b).TotalHours); 

Alternatively, you can do this:

 var sorted = aDates.OrderByDescending(d => d).ToArray(); var results = from i in Enumerable.Range(0, sorted.Length - 1) select sorted[i].Subtract(sorted[i + 1]).TotalHours; 

But this second method will only work List<T> , T[] or any type that supports array index indices.

+11
source share

Another option is to use the aggregation function and return as the totality of the current element. This gives an additional advantage: you repeat the assembly only once:

  public static List<double> GetHoursBetweenDates(List<DateTime> aDates) { List<double> lst = new List<double>(); aDates.OrderByDescending(d => d).Aggregate((prev, curr) => { lst.Add(prev.Subtract(curr).TotalHours); return curr; }); return lst; } 
+9
source share

As an alternative to the solution using the LINQ Zip enumerator, for which you want to iterate through your list twice, here is the custom LINQ operator that iterates through the sequence and returns a "moving pair" of elements:

 static IEnumerable<Tuple<T, T>> Pairwise<T>(this IEnumerable<T> xs) { using (IEnumerator<T> enumerator = xs.GetEnumerator()) { if (!enumerator.MoveNext()) yield break; T current = enumerator.Current; while (enumerator.MoveNext()) { T previous = current; current = enumerator.Current; yield return Tuple.Create(previous, current); } } } 

You can then apply it to your DateTime sequence as follows:

 dates.Pairwise().Select(_ => _.Item2.Subtract(_.Item1).TotalHours); 
+7
source share

You can use the Incremental extension method from moreLINQ :

 public static List<double> GetHoursBetweenDates(List<DateTime> aDates) { return aDates.OrderByDescending(d => d) .Incremental((p,n) => p.Subtract(n).TotalHours) .ToList(); } 

It does exactly what you need:

 /// <summary> /// Computes an incremental value between every adjacent element in a sequence: {N,N+1}, {N+1,N+2}, ... /// </summary> /// <remarks> /// The projection function is passed the previous and next element (in that order) and may use /// either or both in computing the result.< /// If the sequence has less than two items, the result is always an empty sequence. /// The number of items in the resulting sequence is always one less than in the source sequence. /// </remarks> 
+4
source share

All Articles