3

BACKGROUND: I created a generic TimeSeries<T> class that should implement the IEnumerable<IEnumerable<T>> interface:

public interface ITimeSeries<T> : IEnumerable<IEnumerable<T>> 
{
    T[,] DataMatrix { get; }  

    DateTime[] DateTime { get; }

    string[] Variables { get; }
}

public class TimeSeries<T> : ITimeSeries<T>
{
    public IEnumerator<IEnumerable<T>> GetEnumerator()
    {...}

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

The TimeSeries<T> class is 'internally' implemented as a matrix data structure with each column representing a different variable and each row an observation. There is also an additional DateTime[] array that represents the time axis.

As a start I would like to be able to iterate through the TimeSeries<T> variable by variable with a foreach loop and also through all observations for a specific variable (with an inner foreach loop), but the actual reason for the IEnumerable interface implementation is to get all the features provided by LINQ through the IEnumerable interface, provided that I can somehow ensure that the DateTime association of each observation stays intact.

So for the question: How should I go about to implement the GetEnumerator method to achieve this?

nswart
  • 153
  • 2
  • 13
  • 1
    http://blog.slaks.net/2010/12/nested-iterators-part-1.html http://blog.slaks.net/2010/12/nested-iterators-part-2.html – SLaks Dec 05 '12 at 13:59
  • Check out [this][1] answer also here on `StackOverflow`. [1]:http://stackoverflow.com/a/54022/1852004 – tweakch Dec 05 '12 at 14:20

2 Answers2

2

Simplest way is just to loop through the rows and return the values of each row. I'm assuming you're storing in row-major order where the first dimension is the row and the second is the column. Just flip r and c below for column-major order:

public IEnumerator<IEnumerable<T>> GetEnumerator()
{
    int rows = DataMatrix.GetLength(0);
    int cols = DataMatrix.GetLength(1);

     for(int r = 0; r < rows; r++)
     {
        T[] row = new T[cols]();

        for(int c = 0; c < cols; c++)
        {
            row[c] = DataMatrix[r,c];
        }
        yield return row;
    }
}

If you want to avoid making a copy, you could implement it as either a List of Lists or an Array of Arrays (a T[][] instead of a T[,]).

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • I like the solution, but one of my main concerns is that one loses the time context of each row and also the ability to filter by column. The fault however is not with your answer but with the design of my class. Another thing - the reason for using T[,] instead of T[][] is because of interoperability with native code elsewhere in my code, but thanx for the suggestion. – nswart Dec 05 '12 at 15:46
  • Yes you're going to have trouble trying to use Linq if your filter is not part of the returned data (e.g. `timeSeries.Where(date – D Stanley Dec 05 '12 at 16:03
  • I've decided to wrap the values of type T in a struct which also includes the variable name and DateTime stamp. – nswart Dec 05 '12 at 17:36
0

I've decided to add a struct which wraps each value and adds the DateTime stamp and Variable name properties and the GetEnumerator method yields a column of wrapped values:

    public struct TSItem<T>
    {
        public string Variable { get; private set; }
        public DateTime Date { get; private set; }
        public T Value { get; private set; }
    }

    public IEnumerator<IEnumerable<TSItem<T>>> GetEnumerator()
    {
        int rows = DataMatrix.GetLength(0);
        int cols = DataMatrix.GetLength(1);

        for (int c = 0; c < cols; c++)
        {
            var col = new List<TSItem<T>>();

            for (int r = 0; r < rows; c++)
            {
                col.Add(new TSItem<T>(this.Variables[c], this.DateTime[r], 
                                DataMatrix[r, c]));
            }
            yield return col;
        }
    }

This is probably not the most performant solution, but achieves what I want in terms of LINQ queries.

nswart
  • 153
  • 2
  • 13