I have the following Entity Framework class definition:
class TimeValue
{
DateTime StartDate;
double Value;
}
Say given the following series of values I only want to know when the Value changes:
2000-01-01 100
2000-01-15 100
2000-02-01 110
2000-02-15 120
2000-03-01 120
2000-03-15 50
2000-04-01 50
2000-04-15 50
2000-05-01 120
So the result would be:
2000-01-01 100
2000-02-01 110
2000-02-15 120
2000-03-15 50
2000-05-01 120
I can select the values fine using lambda/linq. Then I iterate through the results using the following code to add to a list:
var timeValueQuery = _context.TimeValues.Where(...);
List<TimeValue> timeChanges = new List<TimeValue>();
TimeValue lastValue = null;
foreach (var tvq in timeValueQuery)
{
if (lastValue == null || tvq.Value != lastValue.Value)
{
timeChanges.Add(tvq);
}
lastValue = tvq;
}
Just wondered if there was a quicker/nicer way to do it.
You can create extension method which will return item only if some condition is met. This condition (predicate) will accepts two parameters - previous and current item, which it should compare:
public static IEnumerable<T> TakeIf<T>(this IEnumerable<T> source,
Func<T, T, bool> predicate)
{
var enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
yield break;
yield return enumerator.Current;
T previous = enumerator.Current;
while (enumerator.MoveNext())
{
T current = enumerator.Current;
if (predicate(previous, current))
yield return current;
previous = current;
}
}
Pass predicate which checks if previous and current items have different values. Usage:
var timeChanges = _context.TimeValues.TakeIf((x, y) => x.Value != y.Value);
Edited after reading your dataset again.
I see a grouping wouldn't work. Ideally though, you want this logic on the db server, not client side due to network io, etc.
You can either write this as a proc or dynamic sql. Dynamic sql maybe easier in the near term.
Simple method, select it into a cursor. Loop into a temp table variable and return the result set.
If you dataset is really small, then stick with the easy to read linq loop you have. Don't add more complexity unless you need too. ie don't micro-optimize.
Don't know if this is nicer :) anyway:
IQueryable<TimeValue> values = ...; // initialize here
var query = from v in values
where (from v2 in values
orderby v2.StartDate
where v2.StartDate > v.StartDate
select v2.Value).FirstOrDefault() != v.Value
select v;
If zero value is possible, change FirstOrDefault() to FirstOrDefault(impossible_value). Also for cycle may be more efficient (but you asked for LINQ).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.