3

If I have a list of objects that contains a timestamp - how can I make an observable that fire events at the relative time between the timestamps?

Eg. if I have three objects with timestamps 2014-01-01 10:30, 2014-01-01 10:45 and 2014-01-01 11:30, I want the first event to fire right away, then the next 15 minutes later, and the last one 45 minutes after that.

How can I speed up the process, so that 1 minute equals 1 second? So the first event would fire right away as before, but the next would come 15 seconds later instead of 15 minutes and so on.

Vegar
  • 12,828
  • 16
  • 85
  • 151

2 Answers2

4

Lots of ways to do this I'm sure, but here's a quick sample using Observable.Generate to schedule events at a particular time. It uses a list of events - but given it works with an iterator, you can easily adapt it to use another source:

void Main()
{
    var now = DateTime.Now;

    var events = new List<Tuple<DateTime, string>> {
        Tuple.Create(now.AddSeconds(5), "A"),
        Tuple.Create(now.AddSeconds(10), "B"),
        Tuple.Create(now.AddSeconds(15), "C")
    };

    var eventSource = Observable.Generate(
        (IEnumerator<Tuple<DateTime,string>>)events.GetEnumerator(),
        s => s.MoveNext(),
        s => s,
        s => s.Current.Item2, // the data
        s => s.Current.Item1); // the timing

    eventSource.Subscribe(Console.WriteLine);                       
}

This writes out "A", "B" and "C" and 5, 10 and 15 seconds after start-up.

To speed up time, you can put some logic around how the scheduled time is interpreted. Another way it to use the HistoricalScheduler. See this answer for some insight on that.

Community
  • 1
  • 1
James World
  • 29,019
  • 9
  • 86
  • 120
  • HistoricalScheduler sounds interesting. Never heard about that one before. Observable.Generate( ) also looks nice. Thanks! – Vegar Mar 07 '14 at 20:18
  • This may not work if the dates are out of order. It ``may`` not pull the next one and schedule it before the previous one. Though I'm not sure. The code for Generate in the RX source is complex. – bradgonesurfing Mar 13 '14 at 07:32
  • Yes, it expects events to be in order. I don't want to live in a world where the enumerator supplies them out of order though! That would be mental. If you need to check for it, you could easily enough - but it would smell like you are "doing it wrong (tm)" to me. Also, out of order events against a *running* scheduler could be problematic in all implementations here. Generate is actually nice precisely *because* it only schedules the next event on the completion of the preceding one, and therefore prevents scheduler starvation. – James World Mar 13 '14 at 13:03
2

Easiest way is to merge an enumerable of observables like so

IEnumerable<Tuple<DateTimeOffset,string>> timeStamps = ...;
IObservable<Tuple<DateTimeOffset,string>> eventStamps = timeStamps
    .Select(s => Observable.Timer(s.Item1).Select(e=>s))
    .Merge();

As per James World's answer, to speed or slow time, you can add the historical scheduler as a second argument to Timer. (Give him the points for that bit. I didn't know about that before I read his answer )

Observable.Timer(s.Item1, historicalScheduler)

If you are wondering why DateTimeOffset rather than DateTime look at DateTime vs DateTimeOffset

The nice thing is that the code above works if timeStamps is either IEnumerable or IObservable. There is an overload of merge that works for either.

Community
  • 1
  • 1
bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217
  • ...and if you really *really* care about the `DateTime` vs `DateTimeOffset` debate, look at [nodatime](http://nodatime.org/) – James World Mar 12 '14 at 15:14