Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Is there an elegant solution to walk through an ordered list to do some calculations on the current and the next object? There must be a smarter way with LINQ to 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;
}
like image 478
Seatech Avatar asked Jul 29 '13 19:07

Seatech


People also ask

How do I find the next record in LINQ?

First(); var next = items . OrderBy(item => item.Id) . First(item => item.Id > currentId); var next = items .

Does LINQ select return new object?

While the LINQ methods always return a new collection, they don't create a new set of objects: Both the input collection (customers, in my example) and the output collection (validCustomers, in my previous example) are just sets of pointers to the same objects.

What types of objects can you query using LINQ?

You can use LINQ to query any enumerable collections such as List<T>, Array, or Dictionary<TKey,TValue>. The collection may be user-defined or may be returned by a . NET API. In a basic sense, LINQ to Objects represents a new approach to collections.

What is any () in LINQ?

The Any operator is used to check whether any element in the sequence or collection satisfy the given condition. If one or more element satisfies the given condition, then it will return true. If any element does not satisfy the given condition, then it will return false.


2 Answers

The easiest to compare each consecutive element in a 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 which supports array-style indexers.

like image 73
p.s.w.g Avatar answered Oct 07 '22 09:10

p.s.w.g


As an alternative to a solution using LINQ's Zip enumerator, which requires you to iterate over your list twice, here's a custom LINQ operator that iterates over a 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 could then apply it to your DateTime sequence as follows:

dates.Pairwise().Select(_ => _.Item2.Subtract(_.Item1).TotalHours);
like image 30
stakx - no longer contributing Avatar answered Oct 07 '22 11:10

stakx - no longer contributing