Miguel Moura
Miguel Moura

Reputation: 39384

Implement class to work with real time data

Using C# I need to transform a time series, for example:

Input: 1 2 1 0 3 2 -> Output: 1 3 4 4 7 9

In the previous example an output is the sum of current input with the previous inputs.

I will have different transformations so I started with an abstract class:

public abstract class Transformation<TInput, TOutput> {

  private IEnumerable<TInput> _inputs { get; set; }

  public Transformation(IEnumerable<TInput> inputs) { 
    _inputs = inputs;
  }

  public abstract IEnumerable<TOutput> Calculate();

}

So a transformation would be something like:

public class TransformationOne<Int32, Int32> {

  public override IEnumerable<Int32> Calculate();
    // Calculate the outputs with all inputs
  }

}

This works fine if I have all inputs and want all outputs.

But sometimes I would like to use my class in real time.

So I would feed with a new input and would get a new output.

What would be the best way to implement this?

Upvotes: 1

Views: 117

Answers (4)

Flydog57
Flydog57

Reputation: 7111

My answer is very similar to what @Julian did. However, I had already written my solution, I think it does a better job of providing the two aspects of the OP's question:

  • How to do this for a sequence of data (like what was originally shown)
  • How do use the same capability for one-at-a-time data

You can't easily do this with a generic, there is no way to constrain a type to one that includes addition (if you know of one, let me now). So this is what the transformation class looks like:

public class AccumulateTransformer
{
    private int currentState = 0;

    public void Clear()
    {
        currentState = default(int);
    }

    public int NextValue(int input)
    {
        currentState += input;
        return currentState;
    }

    public int Current => currentState;

    public IEnumerable<int> TransformSequence(IEnumerable<int> inputs, bool clearFirst = true)
    {
        if (clearFirst)
        {
            Clear();
        }

        foreach (var value in inputs)
        {
            yield return NextValue(value);
        }
    }
}

And here's some code to test it (using your data):

 var transformer = new AccumulateTransformer();
 var inputs = new[] {1, 2, 1, 0, 3, 2,};
 var results = transformer.TransformSequence(inputs);
 Debug.WriteLine(string.Join(", ", from result in results select result.ToString() ));

 transformer.Clear();
 Debug.WriteLine(transformer.NextValue(1));
 Debug.WriteLine(transformer.NextValue(2));
 Debug.WriteLine(transformer.NextValue(1));
 Debug.WriteLine(transformer.NextValue(0));
 Debug.WriteLine(transformer.NextValue(3));
 Debug.WriteLine(transformer.NextValue(2));

The output of that looks like:

1, 3, 4, 4, 7, 9
1
3
4
4
7
9

To make this generic, you need a way of doing the accumulation operation that isn't dependent on T understanding addition. To do that, change the code to what's show below.

It makes the type generic on T (which would be int in this case), but includes a constructor that takes a Func<T, T, T> that will do the accumulation. When you construct your instance, you provide a Func that just adds two numbers.

The class code:

public class AccumulateTransformer<T>
{
    private T _currentState = default(T);

    private Func<T, T, T> _accumulator;

    public AccumulateTransformer(Func<T, T, T> accumulator)
    {
        _accumulator = accumulator;
    }

    public void Clear()
    {
        _currentState = default(T);
    }

    public T NextValue(T input)
    {
        _currentState = _accumulator(_currentState, input);
        return _currentState;
    }

    public T Current => _currentState;

    public IEnumerable<T> TransformSequence(IEnumerable<T> inputs, bool clearFirst = true)
    {
        if (clearFirst)
        {
            Clear();
        }

        foreach (var value in inputs)
        {
            yield return NextValue(value);
        }
    }

The only difference in using this is that you have to provide that accumulate Func in the constructor:

 var transformer = new AccumulateTransformer<int>((a, b) => a + b);

Otherwise, it's used the same way and produces the same output.

Upvotes: 1

Ashkan Mobayen Khiabani
Ashkan Mobayen Khiabani

Reputation: 34160

A simple Linq.Select() would do that:

int sum = 0;
var result = input.Select(x => { sum += x; return sum; });

Live Demo

Upvotes: 0

Julian
Julian

Reputation: 36740

Keep the state in the class and send the input to the Calculate method?

class TransformationOne
{
    private int _currentResult;

    public int Calculate(int value)
    {
        _currentResult += value;
        return _currentResult;
    }
}

so you could do:

var transformationOne = new TransformationOne();

var inputs = new List<int> {1, 2, 1, 0, 3, 2};

foreach (var input in inputs)
{
    var newResult = transformationOne.Calculate(input); 
    Console.WriteLine(newResult); // results: 1 3 4 4 7 9
}

Demo

Upvotes: 2

Rufus L
Rufus L

Reputation: 37020

I'm not sure if this is exactly what you're after, but you could simply keep a list of the items in the class, and provide public methods for adding new items to it:

public class Transformer
{
    private readonly List<int> items = new List<int>();
    public IEnumerable<int> Items => items.ToList();
    public void Add(int item) => items.Add(items.LastOrDefault() + item);

    public void Add(IEnumerable<int> input)
    {
        foreach (var item in input) Add(item);
    }

    public override string ToString()
    {
        return string.Join(", ", Items);
    }
}

In use it might look like:

public static void Main(string[] args)
{
    var trans = new Transformer();
    var items = new[] { 1, 2, 1, 0, 3 };

    // Add a bunch of inputs at once:
    trans.Add(items);
    // Display results
    Console.WriteLine($"After adding 5 items: {trans}");
    // Add a single item
    trans.Add(2);
    // Display results
    Console.WriteLine($"After adding 1 more item: {trans}");

    GetKeyFromUser("\nDone! Press any key to exit...");
}

Output

enter image description here

Upvotes: 0

Related Questions