返回内部块可观察对象

Returning Inner Block Observable Object

我想 return 我的 Firebase 数据库中在过去 7 天内质量评级最高的单个对象。我使用以下 Typescript 代码首先查询过去 7 天创建的所有线程,其次提取 quality 处具有最高值的线程。理想情况下,我可以将 "transform" 可观察到的 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
}

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

.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;
}

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);
}

并且在您的 class 构造函数中的某处只需订阅 getWeeksBest$

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

上的示例

如果你想 return 一个在上周发出质量最高的线程的可观察对象,你传递给 map 运算符的函数需要 return 一个值(如其他答案中所述)。

另外,您需要决定在没有线程的情况下预期的行为。结果观察结果应该是空的吗?或者它应该发出 null?

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

你可以这样做,而不是:

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
))