如何在Observable中过滤和转换数据-重复数据

时间:2018-08-11 20:47:08

标签: angular rxjs observable google-cloud-firestore angular6

我有一个主题事件模型-定义了它应该具有的属性:

export class EventSchema {
    title: string;
    start: string;
    end: string;
    price: number;

    constructor(title: string, start: string, end: string, price: number){
        this.title=title;
        this.start=start;
        this.end=end;
        this.price=price;
    }
}

我还有一个封装上述模型的模型-在一个数组中有一个主题名称和事件属性,因为单个主题可以有多个事件:

import { EventSchema } from './eventSchema.model';

export class SubjectEvents {
    subjectName: string;
    eventData: EventSchema[];

    constructor(subjectName: string, eventData: EventSchema[]){
        this.subjectName=subjectName;
        this.eventData=eventData;
    }
}

我在firebase中有一个与这些模型匹配的集合。到目前为止,只有两份文档,一份涉及数学科目,一份涉及物理科目,每份涉及2个事件。示例数据是:firebase data

我有此服务,它获取数据并对其进行转换。在这种情况下,我想将一个主题(文档)的每个事件放入一个大数组中。也许我需要合并每个文档的输出...不确定如何。我能够在某种程度上实现这一目标-但结果重复出现,我不明白为什么。请在下面查看最终输出的屏幕截图。

import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument, QuerySnapshot, QueryDocumentSnapshot } from 'angularfire2/firestore';
import { Observable, of, Subscription } from 'rxjs';
import { map, filter } from 'rxjs/operators';
import 'rxjs/add/operator/mergeMap';
import { SubjectEvents } from '../_models/subjects/subjectEvents.model';
import { EventSchema } from '../_models/subjects/eventSchema.model';

@Injectable()
export class SubjectEventsService {

    subjectEventCollection : AngularFirestoreCollection<SubjectEvents>;
    subjectEventDocument : AngularFirestoreDocument<SubjectEvents>;
    subjectEvents : Observable<SubjectEvents[]>;
    subjectEvent : Observable<SubjectEvents>;
    filteredArr : Observable<any[]>;

    constructor(private afs : AngularFirestore) { 
      //Get user collection on initialise
      this.subjectEventCollection = this.afs.collection('subjectEvents');
    }
    getSubjectEvents(subjectFilter: string): Observable<SubjectEvents[]> {
      this.filteredArr = this.subjectEventCollection.snapshotChanges()
      .pipe(map(changes => {
        let filteredArr : any = [];
        console.log(filteredArr);
        return changes
            .map(action => {
              console.log(action);
              const data = action.payload.doc.data() as SubjectEvents;
              data.subjectName = action.payload.doc.data().subjectName;
              data.eventData = action.payload.doc.data().eventData;
              console.log(data);

              data.eventData.forEach(result => {filteredArr.push(result); console.log(filteredArr)});

              return filteredArr;
        });
      }));
      return this.filteredArr;
    }
}

Duplicated output

2 个答案:

答案 0 :(得分:0)

Live working example

我希望我能帮上忙,这是我的建议:使用filtermap,但是在过滤器中通过自定义函数来达到您的预期结果:

const duplicateData = from(data);
const emmited = [];
const hasBeenEmitted = (item, emmited) => {
  if (emmited.filter(e => e.title !== item.title).length === 0) {
    emmited.push(item);
    return item;
  } else {
    return null;
  }
};

const source = duplicateData.pipe(
  filter(item => hasBeenEmitted(item, emmited)),
  map(e => ({ value: e.price, start: e.start }))
);

source.subscribe(console.log);

答案 1 :(得分:0)

Live example

我提供了一个更好的方法,我创建了一个名为hasBeenEmitted的自定义运算符,您只需要向其提供要过滤数据的属性即可。

const duplicateData = from(data);
const hasBeenEmitted = n => source =>
  new Observable(observer => {
    const emmited = [];
    return source.subscribe({
      next(item) {
        if (emmited.filter(e => e.title !== item.title).length === 0) {
          emmited.push(item);
          observer.next(item);
        }
      },
      error(err) { observer.error(err); },
      complete() { observer.complete(); }
    })
  });



const source = duplicateData.pipe(
  hasBeenEmitted(item => item.title),
  map(e => ({ value: e.price, start: e.start }))
);

source.subscribe(console.log);