How to filter and transform data in Observable - duplicate data

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



How to filter and transform data in Observable - duplicate data



I have a model of subject events - defines what properties it should have:


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;




I have another model that encapsulates the above model - has a subject name and the event properties in an array since there can be multiple events for a single subject:


import EventSchema from './eventSchema.model';

export class SubjectEvents
subjectName: string;
eventData: EventSchema;

constructor(subjectName: string, eventData: EventSchema)
this.subjectName=subjectName;
this.eventData=eventData;




I have a collection in firebase matching these models. So far there are only two documents, one with maths subjects and one with physics subjects, each with 2 events. Example data is: firebase data



I have this service which gets the data, and transforms it. In this instance, I want to take each event for a subject (document) and put it into one big array. Maybe I need to merge the output of each document... not sure how. I am able to achieve this somewhat - but the results are coming out duplicated, and I don't understand why. Please see below a screen shot of the final output.


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





I would say you need to .pipe(mergeMap(changes... or return changes.pipe(mergeMap(action... because changes is an Observable<SubjectEvents> and you want to unwind that array to Observable<SubjectEvents>. It helps with reasoning if you type your parameters. (BTW perhaps bad form to mix pipable operators and non-pipable operators).
– Richard Matsen
Aug 11 at 22:05



.pipe(mergeMap(changes...


return changes.pipe(mergeMap(action...


Observable<SubjectEvents>


Observable<SubjectEvents>




2 Answers
2



Live example



I just came with a better approach, I created a custom operator called hasBeenEmitted, you just has to provide it the property you want to filter your data.


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



Live working example



I hope I can help, this is my propose: use filter and map, but in filter pass through a custom function to achieve your expected results:


filter


map


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






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard