Reputation: 39384
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
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:
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
Reputation: 34160
A simple Linq.Select() would do that:
int sum = 0;
var result = input.Select(x => { sum += x; return sum; });
Upvotes: 0
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
}
Upvotes: 2
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
Upvotes: 0