DarthVegan
DarthVegan

Reputation: 1269

How to get this linq query to access info from the previous row

I'm new to linq and I'm trying to figure out how to change this linq query so that where it says prevPrice I'm accessing the Price from the previous row

var items = Data.Where(i => i.Date <= startingDate)
                .Take(days + 1)
                .Average(i => (i.Price - i.prevPrice) * i.Volume);

Upvotes: 1

Views: 728

Answers (5)

interesting-name-here
interesting-name-here

Reputation: 1890

A little different approach. Linq is pretty much a simpler foreach so you can use self-incrementing notation to get the element before or after.

int i = Data.Count();
int total = Data.Count();

Data = Data.OrderBy(o => o.Date);

var items = Data
    .Where(o => o.Date <= startingDate)
    .Take(days + 1)
    .Average(o => (o.Price - (i == total ? (0 * (i--)) : Data.ElementAt(total - (i--)).Price)) * o.Volume);

Upvotes: 0

code4life
code4life

Reputation: 15794

Just use the overloaded Select() call, like this:

var whatYouNeed = Data.Where(i => i.Date <= startingDate)
            .Take(days + 1)
            .ToList(); // I'm too lazy to be fancy, so I'm gonna preempt the LINQ here.

var items = whatYouNeed
            .Select((item, index) => 
                new 
                { 
                    Current = item, 
                    Previous = whatYouNeed[(index == 0 ? 0 : index - 1)]
                })
            .Average(item => (item.Current.Price - item.Previous.Price) * i.Volume);

Caveat: I'm making it so that if the first element is, literally, the first item in the collection, then it will put the first element (again) as Previous. If you don't like that, then modify the ternary to do what you want it to do (or leave me a comment for a sample).

Hope this helps!

Upvotes: 0

Jonathan Tyson
Jonathan Tyson

Reputation: 463

Using MoreLinq's Pairwise function, extra variables can be avoided:

var items = Data.Where(i => i.Date <= startingDate)
            .Take(days + 1)
            .Pairwise( ( previous, current ) => ( current.Price - previous.Price ) * current.Volume )
            .Average();

Pairwise provides the predecessor and current element as first and second arguments respectively to the resultSelector function, except for the first element which is only provided as a predecessor to the second element.

Upvotes: 0

JuanR
JuanR

Reputation: 7803

This might work. I whipped it up real quick so check for consistency. I will edit when I have a moment:

var items = Data.Where(i => i.Date <= startingDate)
            .Take(days + 1)
            .Average(i => (
            i.Price - Data.Where(p => p.Date < i.Date)
            .OrderByDescending(p => p.Date)
            .FirstOrDefault(p => o.Price)
            ) * i.Volume);

Upvotes: 0

ASpirin
ASpirin

Reputation: 3651

You can use aggregate function to collect all calculations and than get an average from it

var results = new List<int>();
data.Where(i => i.Date <= startingDate).Take(days)
    .Aggregate((a, b) =>
    {
        results.Add((b.price - a.price)*a.Volume);
        return b;
    });
var result = results.Average();

Upvotes: 2

Related Questions