RXJS Observable 数组上的简单过滤器

Posted

技术标签:

【中文标题】RXJS Observable 数组上的简单过滤器【英文标题】:Simple filter on array of RXJS Observable 【发布时间】:2016-10-25 19:02:24 【问题描述】:

我正在使用 Angular2 开始我的项目,开发人员似乎推荐 RXJS Observable 而不是 Promises。

我已经实现了从服务器检索元素列表(史诗)。 但是如何使用例如 id 来过滤元素?

以下代码是从我的应用程序中提取的,现在显示了最终的工作解决方案。让我们希望它对某人有所帮助。

@Injectable()
export class EpicService 

  private url = CONFIG.SERVER + '/app/';  // URL to web API

  constructor(private http:Http) 

  private extractData(res:Response) 
    let body = res.json();
    return body;
  

  getEpics():Observable<Epic[]> 
    return this.http.get(this.url + "getEpics")
      .map(this.extractData)
      .catch(this.handleError);
  

  getEpic(id:string): Observable<Epic> 
    return this.getEpics()
      .map(epics => epics.filter(epic => epic.id === id)[0]);
  


export class EpicComponent 

  errorMessage:string;
  epics:Epic[];
  epic:Epic;

  constructor(
    private requirementService:EpicService) 
  

  getEpics() 
    this.requirementService.getEpics()
      .subscribe(
        epics => this.epics = epics,
        error => this.errorMessage = <any>error);
  

  // actually this should be eventually in another component
  getEpic(id:string) 
    this.requirementService.getEpic(id)
        .subscribe(
        epic => this.epic = epic,
        error => this.errorMessage = <any>error);
  


export class Epic 
  id: string;
  name: string;

提前感谢您的帮助。

【问题讨论】:

你应该保留原来的问题,这样我们就永远不知道“不”做什么 【参考方案1】:

带有修复的原始答案: Observables 很懒惰。你必须打电话给subscribe 告诉observable 发送它的请求。

  getEpic(id:number) 
    return this.getEpics()
           .filter(epic => epic.id === id)
           .subscribe(x=>...);
  

更新到 Rxjs 6:

import filter from 'rxjs/operators';

getEpic(id:number) 
        return this.getEpics()
               .pipe(filter(epic => epic.id === id))
               .subscribe(x=>...);
      

【讨论】:

subscribe的返回类型是一个Subscription,它实现了ISubscription。两者都没有过滤方法。 @StefanRein,在原始问题 this.getEpics() 返回一个 Array 的 observable。所以,filterArray 的方法 你是对的。就在您的回答中,您将过滤器链接在订阅方法之后,该方法没有过滤器方法,因此我的评论【参考方案2】:

您需要过滤实际的数组,而不是围绕它的 observable。 因此,您会将 Observable 的内容(即 Epic[])映射到过滤后的 Epic

getEpic(id: string): Observable<Epic> 
  return this.getEpics()
     .map(epics => epics.filter(epic => epic.id === id)[0]);

然后你就可以subscribegetEpic 做任何你想做的事情。

【讨论】:

看起来很简单,但是:错误:(33, 42) TS2365:运算符“===”不能应用于“字符串”和“数字”类型。我将更新问题,以显示整个组件和服务关系。 好吧,如果是这样,那么您可以从字符串转换为数字或从数字转换为字符串:)【参考方案3】:

您必须订阅 Observables 才能获取数据,因为 http 调用在 javascript 中是异步的。

getEpic(id: number, callback: (epic: Epic) => void) 
    this.getEpics().subscribe(
        epics: Array<Epic> => 
            let epic: Epic = epics.filter(epic => epic.id === id)[0];
            callback(epic);
        
    );

您可以像这样调用该方法:

this.someService.getEpic(epicId, (epic: Epic) => 
    // do something with it
);

【讨论】:

【参考方案4】:

您可以使用ObservableflatMapfilter 方法而不是map 中的JS 数组过滤方法来做到这一点。比如:

this.getEpics() 
    .flatMap((data) => data.epics) // [id: 1, id: 4, id: 3, ..., id: N]
    .filter((epic) => epic.id === id) // checks id: 1, then id: 2, etc
    .subscribe((result) => ...); // do something epic!!!

flatMap 将为过滤提供奇异索引,然后您可以继续处理接下来发生的任何结果。

如果 TypeScript 抛出错误,表明您无法比较字符串和数字,无论您在过滤器中使用 ==,只需在过滤器中的 epic.id 之前添加一个 +,根据 Angular 文档:

    .flatMap(...)
    .filter((epic) => +epic.id === id) // checks id: 1, then id: 2, etc
    .subscribe(...)

示例:

https://stackblitz.com/edit/angular-9ehje5?file=src%2Fapp%2Fapp.component.ts

【讨论】:

这比公认的答案干净得多,并且充分利用了 Rx。正如作者指出的那样,执行此操作的正确方法是使用flatMap 如果史诗是一个数组(如你所建议的那样),则不能将平面映射到data.epics。而且你也不能像你写的那样过滤数组。 @StefanRein 我认为您应该先试用代码然后发表评论 - stackblitz.com/edit/… @mtpultz 是的,我确实从 SO 复制了您的评论作为输入。您对 SO 上的 getEpics() 方法的评论应该是:// epics: [id: 1, id: 2, id: 3, id: 4, id: 5] 而不是像 Stackblitz 示例中的 // [id: 1, id: 4, id: 3, ..., id: N] ......这让我很困惑,谢谢。也应该查看 Stackblitz 示例:) 我已将评论下移了一行

以上是关于RXJS Observable 数组上的简单过滤器的主要内容,如果未能解决你的问题,请参考以下文章

角度过滤器 Observable 数组

Rxjs 过滤器运算符不适用于 Angular2 Observable

RxJS 将 observable 附加到 typescript 中的 observable 数组列表

RxJs Observable:如果为空/过滤则执行函数

在 RxJS Observable 中“展平”数组的最佳方法

如何在 RxJS 中停止 Observable 的间隔