必须有人在那里解决了这个问题。 想象一下,我有一个类定期引发一个关于值变化的事件(例如PropertyChanged) 这个价值只不过是金钱。
现在,我想利用Rx,以便获得最后10分钟增加的总和。例如BufferWithTime没有帮助,因为我总是需要最后10分钟。
我有什么想法可以做到这一点吗?
TIA 马丁
答案 0 :(得分:3)
以下解决方案涉及使用Observable.Scan
将相关事件数据的状态保留在列表的前十分钟内。状态维护为元组列表,其中int
(金钱)和DateTime
为值。
var events = Observable.FromEvent<YourEventArgs>(
h => SomeEvent += h, h => SomeEvent -= h);
var runningSums =
events.Scan(new List<Tuple<int, DateTime>>(),
(l, e) =>
{
var now = DateTime.Now;
// Add last event data to list.
l.Add(Tuple.Create(e.EventArgs.Money, now));
// Return the correct part of the list (everything
// from the last ten minutes).
return l.Where(t => (now - t.Item2) <
TimeSpan.FromMinutes(10)).ToList();
})
.Select(l => l.Sum(t => t.Item1));
runningSums.Subscribe(sum => Console.WriteLine(sum));
编辑:不为每个事件返回新列表的示例:
var events = Observable.FromEvent<YourEventArgs>(
h => SomeEvent += h, h => SomeEvent -= h);
var runningSums =
events.Scan(Tuple.Create(new List<Tuple<int, DateTime>>(),
DateTime.Now - TimeSpan.FromMinutes(10)),
(l, e) =>
{
var now = DateTime.Now;
l.Item1.Add(Tuple.Create(e.EventArgs.Nr, now));
// if (trimming-condition) then trim front of list...
return Tuple.Create(l.Item1, now - TimeSpan.FromMinutes(10));
})
.Select(l => l.Item1.Where(t => t.Item2 > l.Item2).Sum(t => t.Item1));
runningSums.Subscribe(sum => Console.WriteLine(sum));
答案 1 :(得分:1)
好的,请查看以下解决方案。它建立在此前提出的解决方案的基础上,但为了提高效率(以及可读性,我认为),它放弃了纯粹的功能风格。它重用了内置类型Timestamped
来跟踪时间......
欢呼声
public static class RxEntentsions
{
class TimeLimitedList<T>
{
public List<Timestamped<T>> Values = new List<Timestamped<T>>();
TimeSpan span;
public TimeLimitedList(TimeSpan sp) { span = sp; }
public void Add(Timestamped<T> v)
{
Values.Add(v);
Values.RemoveAll(a => a.Timestamp < (DateTime.Now - span));
}
}
public static IObservable<List<Timestamped<TSource>>> SlidingWindow<TSource>(this IObservable<Timestamped<TSource>> source, TimeSpan slidingWindow)
{
return source.Scan0(new TimeLimitedList<TSource>(slidingWindow), (acc, v) => { acc.Add(v); return acc; }).Select(a => a.Values);
}
}
static void Main(string[] args)
{
var gen = Observable.Interval(TimeSpan.FromSeconds(0.25d)).Timestamp();
gen.SlidingWindow(TimeSpan.FromSeconds(1)).Subscribe(slw => {slw.ForEach(e=> Console.WriteLine(e)); Console.WriteLine("--------");});
Console.ReadLine();
}