C # Quoting via IEnumerable for a calculation that uses n previous and n next elements

I often come across an IEnumerable object that I need to perform in order to perform a calculation for each element, which depends on n immediately preceding and following objects.

One common example is the calculation of a moving average, but in other cases the calculation is more complicated than that, and relies on several fields from each of the list items

I am never sure of the best way to structure my loops. Efficiency matters, but usability and readability are important.

  • Sometimes I convert to a list and then use the for loop to get the elements [i-1], [i], [i + 1], and then do my calculations.

  • In other cases, I save it as IEnumerable, but I "cache" the previous pair of elements, so I do not do the calculation for me until I get [i + 1] in the foreach loop.

  • I also considered using a linked list to use the .Previous and .Next methods.

Any suggestions on which technique would be best used?

+7
source share
2 answers

One option is to create an extension method that provides a window that you can use. This will allow you to write your loop in a simple way:

IEnumerable<IList<T>> CreateRollingWindow(IEnumerable<T> items, int size) { LinkedList<T> list = new LinkedList<T>(); foreach(var item in items) { list.AddLast(item); if (list.Count == size) { yield return list.ToList(); list.RemoveFirst(); } } } 

This will allow you to write your algorithms simply as:

 foreach(var window as collection.CreateRollingWindow(5)) { double rollingAverage = window.Average(); // window is IList<T> here } 
+6
source

Here's a simple implementation:

 public static IEnumerable<double> RollingAverage(this IEnumerable<double> values, int count) { var queue = new Queue<double>(); foreach (var v in values) { queue.Enqueue(v); if (queue.Count == count) { yield return queue.Average(); queue.Dequeue(); } } } 

It could probably be improved, but it seems to work ...

EDIT: here's a slightly better version (she doesn't need to list the queue to calculate the average):

 public static IEnumerable<double> RollingAverage(this IEnumerable<double> values, int count) { var queue = new Queue<double>(); double sum = 0.0; foreach (var v in values) { sum += v; queue.Enqueue(v); if (queue.Count == count) { yield return sum / count; sum -= queue.Dequeue(); } } } 
+2
source

All Articles