使用最新元素和更新而不是键和更新进行分组

时间:2019-07-31 00:26:10

标签: c# system.reactive

我有一个Observable,我想对它们进行模10分组。我想使结果成为热门的Observable,并且当一个新订户订阅时,他获得了所有GroupedObservable的播放,但没有获得Key,我想要最新的价值。我也要更新,跳过最新值。

在此示例中,为了简化起见,我们仅将模结果等于5进行工作。但是我希望我的解决方案适用于所有情况。

示例:

-------- 15 ----- 25 ---------...

让我们在上一个示例中添加一些我们将订阅的时间点:

--- S1 ----- 15--S2 --- 25 ----- S3 ----...

预期结果是:

  • S1接收最新消息:15,更新消息:以25开头的可观察值,并且更新%10 == 5,稍后将到达。 说明:S1将在15到达后得到通知,15是最新的元素,因此我希望它立即生效。第二个参数将是一个可观察到的参数,它将在以后生成25个元素,并在以后生成%10 == 5个元素。

  • S2接收最新消息:15,更新消息:以25开头的可观察值,并且更新%10 == 5,稍后将到达。 说明:S2将在订阅时收到通知,15是最新元素,所以我希望它立即生效。第二个参数将是一个可观察到的参数,它将在以后生成25个元素,并在以后生成%10 == 5个元素。

  • S3接收最新消息:25,更新:可观察到的更新%10 == 5,稍后将到达。 说明:S3将在订阅时收到通知,25是最新元素,所以我希望它立即生效。第二个参数是一个可观察的参数,将来会产生%10 == 5个元素。

以下是一些解决方法的尝试:

下面的代码使用Tuple和NUnit。

第一次尝试

[Test]
public void WhenWeGroupByReplaying1()
{
    var subject = new Subject<uint>();

    var observable = subject.GroupBy(t => t%10)
        .Select(t =>
        {
            var connectableObservable = t.Replay(1);
            connectableObservable.Connect();
            return (key: t.Key, updates: connectableObservable);
        }).Replay();

    observable.Connect();

    // I will block on the First of the lambda below
    var getLastAndUpdates = observable
        .Select(t => (first: t.updates.First(),updates: t.updates.Skip(1)));

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[1] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[1] - UPDATE: {t2}"));
    });

    subject.OnNext(15);

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[2] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[2] - UPDATE: {t2}"));
    });

    subject.OnNext(25);

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[3] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[3] - UPDATE: {t2}"));
    });
}

此解决方案将被阻止,如注释所示。

第二次尝试

[Test]
public void WhenWeGroupByReplaying2()
{
    var subject = new Subject<uint>();

    var observable = subject.GroupBy(t => t, t => t, new ModuloEqualityComparer())
        .Select(t =>
        {
            var connectableObservable = t.Publish(t.Key);
            connectableObservable.Connect();
            return (key: t.Key, updates: connectableObservable);
        }).Replay();

    observable.Connect();

    var getLastAndUpdates = observable
        .Select(t => (first: t.updates.First(),updates: t.updates.Skip(1)));

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[1] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[1] - UPDATE: {t2}"));
    });

    subject.OnNext(15);

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[2] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[2] - UPDATE: {t2}"));
    });

    subject.OnNext(25);

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[3] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[3] - UPDATE: {t2}"));
    });
}

private class ModuloEqualityComparer : IEqualityComparer<uint>
{
    public bool Equals(uint x, uint y)
    {
        return x % 10 == y % 10;
    }

    public int GetHashCode(uint obj)
    {
        return (obj % 10).GetHashCode();
    }
}

结果:

[1] - FIRST: 15
[1] - UPDATE: 15
[2] - FIRST: 15
[1] - UPDATE: 25
[2] - UPDATE: 25
[3] - FIRST: 25

预期结果:(确切顺序不正确)

[1] - FIRST: 15
[2] - FIRST: 15
[1] - UPDATE: 25
[2] - UPDATE: 25
[3] - FIRST: 25

第三次尝试

[Test]
public void WhenWeGroupByReplaying3()
{
    var subject = new Subject<uint>();

    var observable = subject.GroupBy(t => (key: t%10, value:t), t => t, new ModuloEqualityComparer2())
        .Select(t =>
        {
            var connectableObservable = t.Publish(t.Key.Item2);
            connectableObservable.Connect();
            return (key: t.Key, updates: connectableObservable);
        }).Replay();

    observable.Connect();

    var getLastAndUpdates = observable
        .Select(t => (first: t.updates.First(),updates: t.updates.Skip(1)));

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[1] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[1] - UPDATE: {t2}"));
    });

    subject.OnNext(15);

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[2] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[2] - UPDATE: {t2}"));
    });

    subject.OnNext(25);

    getLastAndUpdates.Subscribe(t =>
    {
        Console.WriteLine($"[3] - FIRST: {t.first}");
        t.updates.Subscribe(t2 => Console.WriteLine($"[3] - UPDATE: {t2}"));
    });
}

private class ModuloEqualityComparer2 : IEqualityComparer<(uint,uint)>
{
    private readonly ModuloEqualityComparer _moduloEqualityComparer = new ModuloEqualityComparer();
    public bool Equals((uint, uint) x, (uint, uint) y)
    {
        return _moduloEqualityComparer.Equals(x.Item1, y.Item1);
    }


    public int GetHashCode((uint, uint) obj)
    {
        return _moduloEqualityComparer.GetHashCode(obj.Item1);
    }
}

结果:

[1] - FIRST: 15
[1] - UPDATE: 15
[2] - FIRST: 15
[1] - UPDATE: 25
[2] - UPDATE: 25
[3] - FIRST: 25

预期结果:(确切顺序不正确)

[1] - FIRST: 15
[2] - FIRST: 15
[1] - UPDATE: 25
[2] - UPDATE: 25
[3] - FIRST: 25

感谢阅读。

1 个答案:

答案 0 :(得分:0)

我不确定您要达到的目标,但是希望这对您有帮助:

您的代码有几处错误:

  1. .First()已过时是有原因的。您不应该在Rx中使用阻塞代码
  2. .Replay()需要一个虚拟订阅才能正常工作。我不确定这是否是困扰您代码的原因,但是为了实现您的目标,您想要这样做。
  3. 嵌套订阅通常不是一个好主意。我已将嵌套订阅替换为.Merge()

如果这不能解决您的问题,建议修改您的问题以描述您要使用Rx完成的工作。闻起来有点像XY situation

代码如下:

var subject = new Subject<uint>();

var observable = subject.GroupBy(t => t % 10)
    .Select(t => t.Replay(1).RefCount()).Replay().RefCount();

// dummy subscriptions required for Replay to work correctly.
var dummySub = observable.Merge().Subscribe();

observable
    .Select(o => o.Select((t, index) => (t.Key, t.num, index)))
    .Merge()
    .Subscribe(t =>
    {
        if (t.index == 0)
            Console.WriteLine($"[1] - FIRST: {t.num}");
        else
            Console.WriteLine($"[1] - UPDATE: {t.num}");
    });

subject.OnNext(15);

observable
    .Select(o => o.Select((t, index) => (t.Key, t.num, index)))
    .Merge()
    .Subscribe(t =>
    {
        if (t.index == 0)
            Console.WriteLine($"[1] - FIRST: {t.num}");
        else
            Console.WriteLine($"[1] - UPDATE: {t.num}");
    });

subject.OnNext(25);

observable
    .Select(o => o.Select((t, index) => (t.Key, t.num, index)))
    .Merge()
    .Subscribe(t =>
    {
        if (t.index == 0)
            Console.WriteLine($"[1] - FIRST: {t.num}");
        else
            Console.WriteLine($"[1] - UPDATE: {t.num}");
    });
相关问题