简体   繁体   English

是否可以在按键分组的热门 observable 中对最新项目进行采样?

[英]Is it possible to sample the latest item in a hot observable grouped by key?

In Rx.NET, is it possible to sample the latest item in a hot observable grouped by key?在 Rx.NET 中,是否可以在按键分组的热观察中对最新项目进行采样?

For example, if I have an IObservable<Price> , where Price is:例如,如果我有一个IObservable<Price> ,其中Price是:

Price 
- Key
- Bid
- Offer

Let's assume that the IObservable is linked to an external price feed.让我们假设IObservable链接到外部价格馈送。

Am I able to retrieve all latest Price s, grouped by Key , sampled every 1 second using Rx?我是否能够检索所有最新的Price s,按Key分组,每 1 秒使用 Rx 采样一次?

Assuming some observable source , this returns all prices grouped and sampled by key that have come in in the last second.假设有一些可观察的source ,这将返回最后一秒内按关键字分组和采样的所有价格。

var sampled = source
    .GroupBy(p => p.Key)
    .SelectMany(o => o.Sample(TimeSpan.FromSeconds(1)));

If there's some price which hasn't received a message in the last second, that will not be included.如果有一些价格在最后一秒没有收到消息,则不会包含在内。

If you want old prices included, this will work:如果您想要包含旧价格,这将起作用:

var sampled2 = source
    .Scan(ImmutableDictionary<int, Price>.Empty, (state, p) => state.SetItem(p.Key, p))
    .Replay(1)
    .RefCount();
var dummySubscription = sampled2.Subscribe();
var result = Observable.Interval(TimeSpan.FromSeconds(1))
    .SelectMany(_ => sampled2.Take(1).SelectMany(state => state.Values));

Just make sure to dispose of the DummySubscription when done with the result observable.只需确保在完成可观察result处理DummySubscription

Does this do what you want?这是你想要的吗?

IObservable<ImmutableDictionary<string, Price>> sampled =
    Observable
        .Create<ImmutableDictionary<string, Price>>(o =>
        {
            var output = ImmutableDictionary<string, Price>.Empty;
            return
                source
                    .Do(x => output = output.SetItem(x.Key, x))
                    .Select(x => Observable.Interval(TimeSpan.FromSeconds(1.0)).Select(y => output).StartWith(output))
                    .Switch()
                    .Subscribe(o);
        });

A hot observable won't keep any old values in memory but you could catch the last price of each known key yourself, for example in a dictionary.热 observable 不会在内存中保留任何旧值,但您可以自己获取每个已知键的最后价格,例如在字典中。

Please refer to the following sample code.请参考以下示例代码。

Dictionary<string, double> _prices = new Dictionary<string, double>();

GetPrices()
    .Buffer(TimeSpan.FromSeconds(1))
    .Subscribe(prices =>
    {
        if (prices != null && prices.Count > 0)
        {
            var grouped = prices.GroupBy(x => x.Key);
            foreach (var group in grouped)
                _prices[group.Key] = group.Last().Bid;
        }

        //print out the last quote of each known price key
        foreach (var price in _prices)
        {
            Console.WriteLine("Key: " + price.Key + ", last price: " + price.Value);
        }
    });

It should print the last quote of each known key every second.它应该每秒打印每个已知键的最后一个引号。

Here is a polished version of Shlomo's idea , of maintaining the latest values per key using the Scan operator and an ImmutableDictionary as state.这是 Shlomo思想的完美版本,使用Scan运算符和ImmutableDictionary作为状态维护每个键的最新值。 The custom operator below ( SampleByKey ) samples a sequence of key-bearing elements at a specific interval.下面的自定义运算符 ( SampleByKey ) 以特定间隔对一系列带有键的元素进行采样。 Upon each sampling tick an IDictionary<TKey, TSource> is emitted, containing the latest values that have been emitted so far by each key.在每次采样时IDictionary<TKey, TSource>都会发出一个IDictionary<TKey, TSource> ,其中包含每个键迄今为止发出的最新值。

public static IObservable<IDictionary<TKey, TSource>> SampleByKey<TSource, TKey>(
    this IObservable<TSource> source,
    Func<TSource, TKey> keySelector,
    TimeSpan interval,
    IEqualityComparer<TKey> keyComparer = default)
{
    return source
        .Scan(ImmutableDictionary.Create<TKey, TSource>(keyComparer),
            (dict, x) => dict.SetItem(keySelector(x), x))
        .Publish(published => Observable
            .Interval(interval)
            .WithLatestFrom(published, (_, dict) => dict)
            .TakeUntil(published.LastOrDefaultAsync()));
}

Usage example:用法示例:

IObservable<IDictionary<string, Price>> sampled = priceFeed
    .SampleByKey(p => p.Key, TimeSpan.FromSeconds(1.0));

In case that zero elements have been emitted by the source during two samplings, the same dictionary will be emitted consecutively.如果source在两次采样期间发出了零个元素,则将连续发出相同的字典。

This implementation is very similar with one I've posted previously in a question , about how to sample a sequence at a dynamically changeable time interval.这个实现与我之前在一个问题中发布的非常相似,关于如何以动态可变的时间间隔对序列进行采样。

Note: I scraped my previous implementation ( Revision 1 ) as too complex and potentially leaky.注意:我删除了我之前的实现(修订版 1 ),因为它过于复杂且可能存在漏洞。 Shlomo's approach is easier to understand and modify as needed. Shlomo 的方法更容易理解并根据需要进行修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM