IEnumerable To Observable with interval - c#

To generate a "measure" every 5 sec I'm doing something like :
var Events = Observable.
Interval(TimeSpan.FromSeconds(5)).
Select(i => factory.GenerateRandomMeasure())
I would like to do the same but based on an existing Measure collection.
I assume I have to do something like :
var Events = existingList.ToObservable();
But is It possible to do add an interval notion in order to get each list item with a interval? (one item every 5 sec for example)

You can do either of these which work just fine:
(1)
var Events =
Observable
.Interval(TimeSpan.FromSeconds(5))
.Zip(existingList, (i, x) => x)
.Select(i => factory.GenerateRandomMeasure());
(2)
var Events2 =
Observable
.Generate(
0,
x => x < existingList.Count,
x => x + 1,
x => existingList[x],
x => TimeSpan.FromSeconds(5))
.Select(i => factory.GenerateRandomMeasure());
The first is probably more sensible and easier to write. The second is very much worth learning if you don't know it already as .Generate is very powerful and can be used in a lot of places.

Related

Using reactive extensions how I can create a dynamic list with values who can expire

Scenario
I'm receiving differents notification ids every 100 ms (Source1) and I need to do put every id in a Cache with the specific received date, if the id came twice I only update the date. After that I need to search information for the ids invoking a service, when I receive that information on my app, I need to show it ordered by the received date, updating the screen every 5 seconds. If any id is not refreshed in the range of 10 seconds by the Source1, it needs to change of state to display it in a different category or state
Problem
I'm trying to use Reactive Extensions to solve this problem, but I'm not sure if it's the correct technology because:
I don't know where I should have the cache and how to manage those states
How is the best way to manage the concurrency in general to invoke the external service in the meantime I can receive more ids could be new or old
At the end to have clean list as a result of information where I can see which elements are being updated and which of them not.
Can anyone help me? Thanks
It sounds like the .Scan operator might meet your needs.
Try this:
var source = new Subject<int>();
var query =
source
.Scan(new Dictionary<int, DateTime>(), (a, x) =>
{
a[x] = DateTime.Now;
return new Dictionary<int, DateTime>(a);
})
.Select(x => x.OrderByDescending(y => y.Value));
You can test this with the following code:
var values = new [] { 1, 2, 1, 3, 2, 1 };
Observable
.Interval(TimeSpan.FromSeconds(5.0))
.Take(values.Length)
.Select(x => values[x])
.Subscribe(source);
I get:
It's better though to use ImmutableDictionary so then the query looks like this:
var query =
source
.Scan(
new Dictionary<int, DateTime>().ToImmutableDictionary(),
(a, x) => a.SetItem(x, DateTime.Now))
.Select(x => x.OrderByDescending(y => y.Value));
var query =
source
.Scan(ImmutableDictionary<int, DateTime>.Empty, (a, x) => a.SetItem(x, DateTime.Now))
.Select(x => Observable.Interval(TimeSpan.FromSeconds(5.0)).Select(y => x).StartWith(x))
.Switch()
.Select(x => x.OrderByDescending(y => y.Value));
Try this query - it continues to produce values when your source does, but every 5 seconds after the latest value to come out it repeats the last item (unless the source produces a value and it then reset the 5 second timer).

Using rx to subscribe to event and perform logging after time interval

I have a simple use case where:
Receive a notification of events
Perform some action on the event
Print the content after x interval
How can I do the above step in a single Rx pipeline?
Something like below:
void Main()
{
var observable = Observable.Interval(TimeSpan.FromSeconds(1));
// Receive event and call Foo()
observable.Subscribe(x=>Foo());
// After 1 minute, I want to print the result of count
// How do I do this using above observable?
}
int count = 0;
void Foo()
{
Console.Write(".");
count ++;
}
I think this does what you want:
var observable =
Observable
.Interval(TimeSpan.FromSeconds(1))
.Do(x => Foo())
.Window(() => Observable.Timer(TimeSpan.FromMinutes(1.0)));
var subscription =
observable
.Subscribe(xs => Console.WriteLine(count));
However, it's a bad idea to mix state with observables. If you had two subscriptions you'd increment count twice as fast. It's better to encapsulate your state within the observable so that each subscription would get a new instance of count.
Try this instead:
var observable =
Observable
.Defer(() =>
{
var count = 0;
return
Observable
.Interval(TimeSpan.FromSeconds(1))
.Select(x =>
{
Console.Write(".");
return ++count;
});
})
.Window(() => Observable.Timer(TimeSpan.FromMinutes(0.1)))
.SelectMany(xs => xs.LastAsync());
var subscription =
observable
.Subscribe(x => Console.WriteLine(x));
I get this kind of output:
...........................................................59
............................................................119
............................................................179
............................................................239
Remembering that it starts with 0 then this is timing pretty well.
After seeing paulpdaniels answer I realized that I could replace my Window/SelectMany/LastAsync with the simpler Sample operator.
Also, if we don't really need the side-effect of incrementing a counter then this whole observable shrinks down to this:
var observable =
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.Do(x => Console.Write("."))
.Sample(TimeSpan.FromMinutes(1.0));
observable.Subscribe(x => Console.WriteLine(x));
Much simpler!
I would use Select + Sample:
var observable = Observable.Interval(TimeSpan.FromSeconds(1))
.Select((x, i) => {
Foo(x);
return i;
})
.Do(_ => Console.Write("."))
.Sample(TimeSpan.FromMinutes(1));
observable.Subscribe(x => Console.WriteLine(x));
Select has an overload that returns the index of the current value, by returning that and then sampling at 1 minute intervals, you can get the last value emitted during that interval.

Reactive Extensions : Frequency of event

Lets say I have a class like
MyClass { string id, string eventType, datetime ts}
ts is the timestamp of the event and Id is on which I want to calculate frequency
I have a hot observable of MyClass , I want to calculate number of events recvd per stringId in the last 30 seconds
If number of events is more than 5 , I raise another event of MyClass (with same Id, and eventType ="New" ) and if it falls down below 3 again , I need to update the previously raised event (with same Id, and eventType ="New" ).
I think I need to use sliding window, I have reached so far
public static IObservable<MyClass> CountFrequency(this IObservable<MyClass> source, TimeSpan withinPeriod, string marker)
{
// var scheduler = new HistoricalScheduler();
// var driveSchedule = source.Subscribe(e => scheduler.AdvanceTo(e.Timestamp));
return source.Window(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(5))
.SelectMany(sl => sl)
.GroupBy(a => a.id)
.SelectMany(go => go
.Aggregate(new MyClass(), (acc, evt) => CustomAggFrequency(acc, evt, marker))
.Select(count => count)));
}
I am not able to understand
a) How to relate scheduler to the timestamp of the data not system time
b) How to code the logic of CustomAggFrequency
Any suggestions
Will this not work? Your question is phrased such that it's hard to figure out what you want.
var span = TimeSpan.FromSeconds(30);
var shift = TimeSpan.FromSeconds(5);
var query = source
.Window(span, shift)
.Select(window => window
.GroupBy(item => item.id)
.ToDictionary(g => x.Key, g => g.Count() / span.TotalSeconds));
This query will emit a dictionary every 5 seconds that maps each id to its frequency in hertz during the last 30 seconds. Its type is IObservable<Dictionary<string, double>>.

Subscribers are missing messages; Is this a bug with Rx or its me doing it wrong?

I have a single Window WPF application with the following constructor
numbers = Observable.Generate(DateTime.Now,
time => true,
time => DateTime.Now,
time => { return new Random(DateTime.Now.Millisecond).NextDouble() * 99 + 2; },
time => TimeSpan.FromSeconds(1.0));
numbers.ObserveOnDispatcher()
.Subscribe(s => list1.Items.Add(s.ToString("##.00")));
numbers.Where(n => n < 10).ObserveOnDispatcher().
Subscribe(s => list2.Items.Add(s.ToString("##.00")));
Now here is the screenshot of the lists - Notice 3.76 is missing from the left list... This behavior is intermittent.
The short answer is that you are doing it wrong. Rx is working perfectly.
When you create an observable you are creating the definition of a sequence of values over time, not the actual sequence of values over time. This means that whenever you have a subscriber to the observable you are creating new instances of the observable for each subscriber.
So, in your case you have two instances of this sequence operating:
var numbers =
Observable
.Generate(
DateTime.Now,
time => true,
time => DateTime.Now,
time => new Random(DateTime.Now.Millisecond)
.NextDouble() * 99 + 2,
time => TimeSpan.FromSeconds(1.0));
Now, since you are subscribing to this observable twice in immediate succession the two instances of this observable would be trying to generate values at almost the same time. So the value DateTime.Now.Millisecond would be the same most of the time, but now always. The value returned from new Random(x).NextDouble() is the same for the same x, so hence why most of the time you get the same value from the two instances of the observable. It's just when DateTime.Now.Millisecond is different that you get two different values and it appears that the subscribers are missing values.
Here's an alternative version that should work as you expected initially:
var rnd = new Random((int)DateTime.Now.Ticks);
var numbers =
Observable
.Generate(0, n => true, n => 0,
n => rnd.NextDouble() * 99 + 2,
n => TimeSpan.FromSeconds(1.0));
var publishedNumbers = numbers.Publish();
publishedNumbers
.ObserveOnDispatcher()
.Subscribe(s => list1.Items.Add(s.ToString("##.00")));
publishedNumbers
.Where(n => n < 10)
.ObserveOnDispatcher()
.Subscribe(s => list2.Items.Add(s.ToString("##.00")));
publishedNumbers.Connect();

How to efficiently limit and then concatenate a result with a linq / lambda expression?

I am in the process of creating a service to make it easy for a user to select a protocol from the IANA - Protocol Registry.
As you might imagine searching the registry for the term http pulls up a lot of hits. Since amt-soap-http is going to selected by a user much less frequently than straight http I decided that it would be a good idea to pull out everything that starts with http and then concatenate that with the remaining results.
The below lambda expression is the result of that thought process:
var records = this._ianaRegistryService.GetAllLike(term).ToList();
var results = records.Where(r => r.Name.StartsWith(term))
.OrderBy(r => r.Name)
.Concat(records.Where(r => !r.Name.StartsWith(term))
.OrderBy(r => r.Name))
.Take(MaxResultSize);
Unfortunately, I feel like I am iterating through my results more times than necessary. Premature optimization considerations aside is there a combination of lambda expressions that would be more efficient than the above?
It might be more efficient as a two-step ordering:
var results = records.OrderBy(r => r.Name.StartsWith(term) ? 1 : 2)
.ThenBy(r => r.Name)
.Take(MaxResultSize);
Using comment to explain what I am trying to do is getting hard. So i will post this another answer.
Suppose I want to sort a list of random integers first according to its being even or odd then in numerical order (simulating StartsWith with mod 2).
Here is the test case: action2 is the same as other answer.
If you run this code you will see that my suggestion (action1) is two times faster.
void Test()
{
Random rnd = new Random();
List<int> records = new List<int>();
for(int i=0;i<2000000;i++)
{
records.Add(rnd.Next());
}
Action action1 = () =>
{
var res1 = records.GroupBy(r => r % 2)
.OrderBy(x => x.Key)
.Select(x => x.OrderBy(y => y))
.SelectMany(x => x)
.ToList();
};
Action action2 = () =>
{
var res2 = records.OrderBy(x => x % 2).ThenBy(x => x).ToList();
};
//Avoid counting JIT
action1();
action2();
var sw = Stopwatch.StartNew();
action1();
long t1 = sw.ElapsedMilliseconds;
sw.Restart();
action2();
long t2 = sw.ElapsedMilliseconds;
Console.WriteLine(t1 + " " + t2);
}

Categories