【问题标题】:Filter an observable using values from another observable使用来自另一个可观察对象的值过滤可观察对象
【发布时间】:2023-04-08 01:57:01
【问题描述】:

我有两个可观察的:

  1. 表示复选框输入列表的可观察对象。
  2. 表示来自服务器的事件流的可观察对象。

我想使用来自第一个的值过滤第二个 observable。

从服务器接收到的值包括一个tag 属性,它对应于复选框列表中的值。上述两者结合产生的 observable 只会产生来自服务器的值,其 tag 属性包含在选中的复选框中。

【问题讨论】:

    标签: rxjs reactive-extensions-js


    【解决方案1】:

    您可以使用withLatestFrom.

    source.withLatestFrom(checkboxes, (data, checkbox) => ({data, checkbox}))
      .filter(({data, checkbox}) => ...)
    

    这里,checkboxes 是一个表示复选框输入列表的可观察对象。 source 是一个 observable,表示来自服务器的事件流。在过滤功能中,您可以检查数据是否与复选框设置相比有效并让它通过。

    请注意,checkboxes 在流可以发出任何东西之前发出至少 1 个值是很重要的。

    附言。关于其他答案,即使来源是cold,此解决方案也有效。

    【讨论】:

    • @IonuțG.Stan 确实如此。令人惊讶的是有多少人仍然在参考这些老问题,所以希望它会帮助一些人:)
    • 您应该注意,这是用@Experimental 注释的,并且可能随时显着更改,因此不应在生产代码中使用或依赖它。跨度>
    • @acrespo 这在许多地方的生产代码中使用。你到底是在哪里找到这个@Experimental 标签的?
    • 哦,对不起,我来到这个问题是为了寻找一些 RxJava 见解(同样的问题,但对于 java),我没有注意到 rxjs 标签。所以我添加了该注释,因为withLatestFrom 运算符在java implementation 中用@Experimental 注释(假设RxJava 版本为1.X)。我的错!
    • @acrespo 据我所知withLatestFrom 在 RxJava 2 中没有实验标签。
    【解决方案2】:

    为了使用流B的值过滤流A,您需要观察流B并使用最新的值来过滤流A。

    使用switch() 将 B observable 转换为 observable,从 A observable 产生值。

    checkedInputValuesSource
        .map(function (options) {
            return dataSource
                .filter(function (value) {
                    return options.indexOf(value) !== -1;
                });
        })
        .switch()
        .subscribe(function (x) {
            console.log('out: ' + x);
        });
    

    使用switch() 假定dataSourcehot observable

    使用interval() 生成虚拟数据的示例:

    var input,
        checkedInputValuesSource,
        dataSource;
    
    input = document.querySelectorAll('input');
    
    // Generate source describing the current filter.
    checkedInputValuesSource = Rx.Observable
        .fromEvent(input, 'change')
        .map(function () {
            var inputs = document.querySelectorAll('input'),
                checkedInputValues = [];
            
            [].forEach.call(inputs, function (e) {
                if (e.checked) {
                    checkedInputValues.push(e.value);
                }
            });
            
            return checkedInputValues;
        })
        .startWith([]);
    
    // Generate random data source (hot).
    dataSource = Rx.Observable
        .interval(500)
        .map(function () {
            var options = ['a', 'b', 'c'];
        
            return options[Math.floor(Math.floor(Math.random() * options.length))];
        })
        .do(function (x) {
            console.log('in: ' + x);
        })
        .share();
    
    checkedInputValuesSource
        .map(function (options) {
            return dataSource
                .filter(function (value) {
                    return options.indexOf(value) !== -1;
                });
        })
        .switch()
        .subscribe(function (x) {
            console.log('out: ' + x);
        });
    <script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>
    
    <input type='checkbox' value='a'>
    <input type='checkbox' value='b'>
    <input type='checkbox' value='c'>

    此示例将产生类似于以下内容的输出:

    in: c
    in: a
    out: a
    in: b
    in: c
    out: a
    in: b
    in: a
    

    in 反映所有生成的输入,b 反映通过过滤器的数据。过滤器通过检查复选框输入来调整,反映值“a”、“b”和“c”。

    【讨论】:

    • 这不正是我在解决方案中写的吗?
    • 可能是。我正在检查每个 rxjs 问题/答案,并尽我所能做出贡献。您的答案链接到外部资源,并且需要额外的依赖项才能运行。我已经写了一个沙盒环境中的解决方案。
    • 请注意,即使源很冷,您也可以完成这项工作。与publish(...)。像这样:dataSource .publish(dsrc =&gt; checkedInputValuesSource .switchMap((options) =&gt; dsrc .filter((value) =&gt; options.indexOf(value) !== -1) ) )
    【解决方案3】:

    显然,我需要的是 selectfilterswitchLatest 的组合。我写了一个小测试用例来证明这一点:https://gist.github.com/igstan/d5b8db7b43f49dd87382#file-observable-filter-observable-js-L36-L45

    【讨论】:

    • 要清楚,您的要点使用 mapfilter,我认为它们只是您为 selectwhere 制作的别名?
    • @Brandon 是的,没错。
    • 嗨,我想在 github 上玩一下你的代码,我还需要哪些其他 js 库?
    • @GüntherSchmidt 我已经将运行测试所需的所有内容放在了一个单独的存储库中:github.com/igstan/rx-testing-demo。玩得开心!
    • 非常感谢!与此同时,我一直在学习如何使用一些组合器,并且“combineLatest”在一个特别糟糕的问题中拯救了这一天。
    【解决方案4】:

    扩展来自@Dorus 的答案...在 Kotlin 中,您可以这样做:

    val observable: Observable<Data> = ...
    val filter: Observable<Checkbox> = ...
    val filtered: Observable<Data> =
            observable.filterWithLatestFrom(filter) { checkbox -> checkbox.isSelected }
    

    使用扩展功能:

    /**
     * Return an [Observable] with type [T1] that is filtered using the last event emitted by the [other] observable.
     */
    fun <T1 : Any, T2 : Any> Observable<T1>.filterWithLatestFrom(other: Observable<T2>, filterFunction: (T2) -> Boolean)
    : Observable<T1> {
        return this.withLatestFrom(other) { obs1, obs2 -> Pair(obs1, obs2) }
            .filter { (_, obs2) -> filterFunction.invoke(obs2) }
            .map { (obs1, _) -> obs1}
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      • 2017-07-12
      相关资源
      最近更新 更多