【问题标题】:Order of BehaviorSubject elements is reversing expected outputBehaviorSubject 元素的顺序与预期输出相反
【发布时间】:2021-04-15 21:28:39
【问题描述】:

我有以下代码正在创建事件,这些事件在下面解释的情况下,发出回前面:

var feed = new Subject<double>();
var levels = new BehaviorSubject<double[]>(new[] { 400.0, 500.0, 600.0, 700.0 });

levels
    .Select(thresholds => feed
        .Buffer(2, 1)
        .Where(x => x.Count == 2)
        .Select(x => new { LevelsCrossed = thresholds.GetCrossovers(x[0], x[1]), Previous = x[0], Current = x[1] })
        .Where(x => x.LevelsCrossed.Any())
        .SelectMany(x => x.LevelsCrossed.Select(level => new ThresholdCrossedEvent(level, x.Previous, x.Current))))
    .Switch()
    .DistinctUntilChanged(x => x.Threshold)
    .Subscribe(x => Console.WriteLine(JsonConvert.SerializeObject(x)));

feed.OnNext(520.0);
feed.OnNext(400.0);
feed.OnNext(450.0);
feed.OnNext(650.0);  

交叉检测扩展如下:

public static class ThresholdExtensions
{
    public static IEnumerable<double> GetCrossovers(this double[] self, double previous, double current)
    {
        return self
            .Where(level => level >= previous && level <= current || level <= previous && level >= current);
    }
}

前 2 个发射对象按顺序排列,第二个发射对象向后

// the order of first 2 emitted objects should be reversed
{"Threshold":400.0,"Previous":520.0,"Current":400.0,"SlopeDirection":-1}
{"Threshold":500.0,"Previous":520.0,"Current":400.0,"SlopeDirection":-1}

// the below ordered is OK
{"Threshold":400.0,"Previous":400.0,"Current":450.0,"SlopeDirection":1}
{"Threshold":500.0,"Previous":450.0,"Current":650.0,"SlopeDirection":1}
{"Threshold":600.0,"Previous":450.0,"Current":650.0,"SlopeDirection":1}

前 2 个元素的反转输出与级别的顺序有关 BehaviorSubject - 但是反转此顺序会导致后 3 个元素反转。

如何修改 DetectThresholds 以在传入的提要穿过对象时以正确的顺序发出对象?

上面的工作示例如下:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;

namespace ConsoleApp1
{
    public class ThresholdCrossedEvent
    {
        public ThresholdCrossedEvent(double level, double previous, double current)
        {

            Threshold = level;
            Previous = previous;
            Current = current;
        }

        public double Threshold { get; set; }
        public double Previous { get; set; }
        public double Current { get; set; }
        public int SlopeDirection => Current >= Previous ? 1 : -1;
    }

    public static class ThresholdExtensions
    {
        public static IEnumerable<double> GetCrossovers(this double[] self, double previous, double current)
        {
            return self
                .Where(level => level >= previous && level <= current || level <= previous && level >= current);
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            var feed = new Subject<double>();
            var levels = new BehaviorSubject<double[]>(new[] { 400.0, 500.0, 600.0, 700.0 });

            levels
                .Select(thresholds => feed
                    .Buffer(2, 1)
                    .Where(x => x.Count == 2)
                    .Select(x => new { LevelsCrossed = thresholds.GetCrossovers(x[0], x[1]), Previous = x[0], Current = x[1] })
                    .Where(x => x.LevelsCrossed.Any())
                    .SelectMany(x => x.LevelsCrossed.Select(level => new ThresholdCrossedEvent(level, x.Previous, x.Current))))
                .Switch()
                .DistinctUntilChanged(x => x.Threshold)
                .Subscribe(x => Console.WriteLine(JsonConvert.SerializeObject(x)));

            feed.OnNext(520.0);
            feed.OnNext(400.0);
            feed.OnNext(450.0);
            feed.OnNext(650.0);            

            Console.ReadKey();
        }
    }
}

【问题讨论】:

  • 嗨 morleyc。很抱歉投反对票。由于值的相似性,我发现提供的示例不必要地繁琐。有一堆以415 开头的数字,仅在小数部分有所不同。更糟糕的是,这部分并没有始终如一地代表。在一个地方是415.80,在另一个地方是415.8。试图在我的记忆中记住什么是重要的,而忽略什么是噪音,这对我的心理能力来说是一个太大的挑战,这就是投反对票的基础。这只是反映了我个人无法把握这个问题的本质。
  • 嗨@Theodor,请不要抱歉,您的观点是完全有效的,经过审查,我同意。感谢您的反馈,我从中学到了并试图集中精力消除臃肿。
  • 现在好多了,但是查询对我来说还是有点太复杂了。仅供参考,我撤销了我的反对票。
  • 感谢 Theodor - 我设法修复它并发布了答案,一个很好的反对意见和我需要修复的上下文有助于以新的头脑重新审视。谢谢你花时间看我很感激。

标签: c# system.reactive


【解决方案1】:

需要通过以下方式订购交叉LevelsCrossed = thresholds.GetCrossovers(x[0], x[1]).OrderByWithDirection(x =&gt; x, x[0] &gt; x[1])

static class OrderByExtensions
{
    public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource, TKey>
           (this IEnumerable<TSource> source,
            Func<TSource, TKey> keySelector,
            bool descending)
    {
        return descending ? source.OrderByDescending(keySelector)
                          : source.OrderBy(keySelector);
    }

    public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>
        (this IQueryable<TSource> source,
         Expression<Func<TSource, TKey>> keySelector,
         bool descending)
    {
        return descending ? source.OrderByDescending(keySelector)
                          : source.OrderBy(keySelector);
    }
}

levels
    .Select(thresholds => feed
        .Buffer(2, 1)
        .Where(x => x.Count == 2)
        .Select(x => new {
                       LevelsCrossed = thresholds.GetCrossovers(x[0], x[1]).OrderByWithDirection(x => x, x[0] > x[1]),
                       Previous = x[0],
                       Current = x[1] })                    
        .Where(x => x.LevelsCrossed.Any())
        .SelectMany(x => x.LevelsCrossed.Select(level => new ThresholdCrossedEvent(level, x.Previous, x.Current))))
     .Switch()
     .DistinctUntilChanged(x => x.Threshold)
     .Subscribe(x => Console.WriteLine(JsonConvert.SerializeObject(x)));

使用{ 400.0, 500.0, 600.0, 700.0 } 的级别,输出现在是正确的:

{"Threshold":500.0,"Previous":520.0,"Current":400.0,"SlopeDirection":-1}
{"Threshold":400.0,"Previous":520.0,"Current":400.0,"SlopeDirection":-1}
{"Threshold":500.0,"Previous":450.0,"Current":750.0,"SlopeDirection":1}
{"Threshold":600.0,"Previous":450.0,"Current":750.0,"SlopeDirection":1}
{"Threshold":700.0,"Previous":450.0,"Current":750.0,"SlopeDirection":1}

下面的例子:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive.Linq;
using System.Reactive.Subjects;

namespace ConsoleApp1
{
    public class ThresholdCrossedEvent
    {
        public ThresholdCrossedEvent(double level, double previous, double current)
        {

            Threshold = level;
            Previous = previous;
            Current = current;
        }

        public double Threshold { get; set; }
        public double Previous { get; set; }
        public double Current { get; set; }
        public int SlopeDirection => Current >= Previous ? 1 : -1;
    }

    public static class ThresholdExtensions
    {
        public static IEnumerable<double> GetCrossovers(this double[] self, double previous, double current)
        {
            return self
                .Where(level => level >= previous && level <= current || level <= previous && level >= current);
        }
    }

    static class OrderByExtensions
    {
        public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource, TKey>
               (this IEnumerable<TSource> source,
                Func<TSource, TKey> keySelector,
                bool descending)
        {
            return descending ? source.OrderByDescending(keySelector)
                              : source.OrderBy(keySelector);
        }

        public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>
            (this IQueryable<TSource> source,
             Expression<Func<TSource, TKey>> keySelector,
             bool descending)
        {
            return descending ? source.OrderByDescending(keySelector)
                              : source.OrderBy(keySelector);
        }
    }

    class Program
    {
   
        static void Main(string[] args)
        {
            var feed = new Subject<double>();
            var levels = new BehaviorSubject<double[]>(new[] { 400.0, 500.0, 600.0, 700.0 });

            levels
                .Select(thresholds => feed
                    .Buffer(2, 1)
                    .Where(x => x.Count == 2)
                    .Select(x => new { LevelsCrossed = thresholds.GetCrossovers(x[0], x[1]).OrderByWithDirection(x => x, x[0] > x[1]), Previous = x[0], Current = x[1] })                    
                    .Where(x => x.LevelsCrossed.Any())
                    .SelectMany(x => x.LevelsCrossed.Select(level => new ThresholdCrossedEvent(level, x.Previous, x.Current))))
                .Switch()
                .DistinctUntilChanged(x => x.Threshold)
                .Subscribe(x => Console.WriteLine(JsonConvert.SerializeObject(x)));

            feed.OnNext(520.0);
            feed.OnNext(400.0);
            feed.OnNext(450.0);
            feed.OnNext(750.0);            

            Console.ReadKey();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-15
    • 2019-06-25
    • 1970-01-01
    • 2010-12-13
    • 2015-12-09
    相关资源
    最近更新 更多