【问题标题】:Returning Inner Block Observable Object返回内部块可观察对象
【发布时间】:2017-06-17 13:47:31
【问题描述】:

我想从我的 Firebase 数据库中返回一个在过去 7 天内质量评级最高的对象。我正在使用以下 Typescript 代码,首先,查询过去 7 天内创建的所有线程,其次,提取在 quality 处具有最高值的线程。理想情况下,我可以将 results 可观察对象“转换”为 forEach 语句的最终迭代对象。

highestWeek: Number = 0;

...

getWeeksBest(): Observable<any> {
    let now = Date.now() / 1000;
    let results = this.af.database.list('/threads', {
        query: {
            orderByChild: 'created',
            startAt: now - 604800 //One week ago
        }
    })
    .map(results => {
        results.forEach(result => {
            if (result.quality > this.highestWeek) {
                console.log(result) //Logs next highest quality thread
                this.highestWeek = result.quality       
            }
        })
    })
    results.subscribe(results => console.log(results)) //Undefined
    return results //Undefined
}

【问题讨论】:

  • 在您的.map 中,您并没有改变结果,而是产生了副作用。如果您可以立即转换results,请在映射函数中进行转换并return 转换后的值,如果您想做异步操作,请使用flatMap - 使用该运算符,您可以映射到其他可观察对象。

标签: angular typescript rxjs angularfire2 angular2-observables


【解决方案1】:

所以问题可能是为什么你在subscribe(...) 回调中得到undefined results 变量。

.map() 中,您使用短箭头语法来创建代码块,但这意味着您必须使用return 语句。

.map(results => {
   results.forEach(result => {
       if (result.quality > this.highestWeek) {
           console.log(result) //Logs next highest quality thread
           this.highestWeek = result.quality       
       }
   });
   return results; 
})

在您的情况下,我最好使用 .do() 运算符而不是 .map()

getWeeksBest() 的末尾,您将返回 Observable,因此最后一行肯定不会返回 undefined

getWeeksBest(): Observable<any> {
    let now = Date.now() / 1000;
    let results = this.af.database.list('/threads', {
        ...
    });
    return results;
}

【讨论】:

    【解决方案2】:

    martin 是对的,您需要添加 return 语句。但也要记住 Array.forEach 总是返回undefined。所以在你的情况下,你可以使用Array.map

    但总的来说,我会建议这样的事情:

    getAllThreads$(): Observable<any[]> {
      let now = Date.now() / 1000;
      return this.af.database.list('/threads', {
        query: {
            orderByChild: 'created',
            startAt: now - 604800 //One week ago
        }
      })
    }
    
    getWeeksBest$():Observable<number>{
      return this.getAllThreads$().map(threads => Array.isArray(threads )? threads.reduce((highestQuality, thread) => {
       let currentQuality = thread? thread.quality: 0;
       return currentQuality > highestQuality? currentQuality: highestQuality;
      }, 0): 0);
    }
    

    在你的类构造函数的某个地方订阅getWeeksBest$

    【讨论】:

      【解决方案3】:

      RxJS 提供了许多操作符来执行数据转换。在您的情况下,您想要检索具有最高 quality 值的对象,因此我们可以使用 reduce 运算符,我们将在其中应用 maxBy() 函数。

      const weeks = [
          {
            quality: 1
          },
          {
            quality: 15
          },
          {
            quality: 5
          },
          {
            quality: 8
          }
        ];
      
      
      //maxBy stateless function (you can see a currified function)
      const maxBy = (prop) => (a, b) => a[prop] > b[prop] ? a : b;
      
      //Fetch data and apply the maxBy when the reduce is perform over the collection
      function fetchData() {
        //You can perform some async actions
        return Rx
          .Observable
          .from(weeks)
          .reduce(maxBy('quality'), 0);
      }
      
      fetchData().subscribe(x => console.log(x));
      

      请随时查看Plunker上的示例

      【讨论】:

      • Trypescript 出于某种原因抱怨 from,而 import 'rxjs/add/operator/from'; 并没有改变这一点。
      • 一定要导入 Observable、from 和 reduce 运算符,或者整个 rxJs。你可以查看我之前发的帖子Here
      • 我已经导入了Observable,但由于某种原因,我无法使用上述评论中的语句导入from
      【解决方案4】:

      如果您想返回一个在上周发出最高质量线程的 observable,则您传递给 map 运算符的函数需要返回一个值(如其他答案中所述)。

      此外,如果没有线程,您需要确定应该预期的行为。结果 observable 是否应该为空?还是应该发出null

      可能最好避免内部订阅 - 您将在每次通话时进行额外订阅,并且永远不会取消订阅。而且最好避免副作用,因为发出的线程将提供最高质量。

      您可以这样做:

      getWeeksBest(): Observable<any> {
      
          let now = Date.now() / 1000;
          let best = this.af.database.list('/threads', {
              query: {
                  orderByChild: 'created',
                  startAt: now - 604800 //One week ago
              }
          })
      
          // Filter out empty lists of threads, so that the resultant
          // observable emits nothing if there are no threads:
      
          .filter(threads => threads.length > 0)
      
          // Use Array.prototype.reduce to return the thread with the
          // highest quality:
      
          .map(threads => threads.reduce(
            (acc, thread) => thread.quality > acc.quality ? thread : acc
          ));
          return best;
      }
      

      如果你想在没有线程的情况下发出null,请删除filter并将map更改为:

      .map(threads => (threads.length === 0) ? null : threads.reduce(
        (acc, thread) => thread.quality > acc.quality ? thread : acc
      ))
      

      【讨论】:

      • 这个解决方案直接解决了我的具体问题。谢谢,@cartant。
      • 在速记函数和可观察运算符之间,这两个对我来说都是新的,我正在忘记我的函数中发生了什么。我想,我首先会从阅读速记函数中受益。虽然这里的核心问题是理解如何将 FirebaseListObservable 转换为对象。那里发生的事情对我来说仍然很模糊。您是否知道任何深入探讨该领域的博文?
      • 这似乎很好地解释了array reduce。对于 RxJS:RxJS In-DepthIntro to Reactive Programming;和Don’t Unsubscribe
      • RxJS5 文档在很大程度上是一项正在进行的工作,但有很多有用的RxJS4 documentation。版本之间的重大更改是detailed here
      猜你喜欢
      • 1970-01-01
      • 2019-01-30
      • 2018-04-11
      • 2017-07-12
      • 2018-12-16
      • 1970-01-01
      • 2019-04-10
      • 2019-02-18
      • 2020-11-05
      相关资源
      最近更新 更多