Как отфильтровать наблюдаемый ответ обернутого файла json?

#arrays #json #angular #typescript

#массивы #json #угловой #typescript

Вопрос:

Я работаю над проектом angular, и моя задача — отфильтровать огромный файл на основе ключа «_type», который может принимать разные значения. Прямо сейчас я хочу сначала отфильтровать _type = «COMPETITION».

Моя модель определена в файле competition.model.ts, который выглядит следующим образом:

 export interface Competition {
  product: { active: true };
  schemaVersion: number;      // 2,
  status: string;             // PUBLISHED",
  comp: string;               // "4fc16b10-b8b4-4a99-b9f1-842f0d8b8413",
  _createdDate: number;       // 1594249198,
  discipline: string;         // "TRAP [ACTA]",
  categories: any;            // ["OPEN", "LADIES", "JUNIOR", "VETERAN", "CLAYS_ONLY"],
  host: string;               // "2",
  changeSet: number;          // 257,
  sync: number;               // 155,
  seq: number;                // 120,
  id: string;                 // "4fc16b10-b8b4-4a99-b9f1-842f0d8b8413",
  _type: string;              // "COMPETITION",
  when: number;               // 1597154400,
  title: string;              // "ACTA Self Nom Test"
}
  

Вот мой класс обслуживания, в котором я пытаюсь реализовать это:

 import { Injectable, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/Operators';
import { Competition } from '../interfaces/competition.model';

@Injectable ({providedIn: 'root'})

export class CompetitionListService {

  private loadedCompetitions: Competition[];
  private url = '../../assets/data/program1.json';

  constructor(private http: HttpClient) {}


  public getCompetitions(): Competition[] { return this.loadedCompetitions; }

  public fetchCompetition(){
    return this.http
    .get<Competition[]>(this.url)
      .pipe(
        map( (responseData) => {
          const competitionsArray = [];
          for (const key in responseData ) { // responseData is an object
            if (responseData.hasOwnProperty(key)) {
              // get the_type property
              // if ( key.valueOf() === 'COMPETITION') {
                competitionsArray.push(
                 // responseData[key]._createdDate,
                 responseData[key]._createdDate,
                  responseData[key]._type,
                  responseData[key].categories,
                  responseData[key].changeSet,
                  responseData[key].comp,
                  responseData[key].discipline,
                  responseData[key].host,
                  responseData[key].id,
                  responseData[key].product,
                  responseData[key].schemaVersion,
                  responseData[key].seq,
                  responseData[key].status
                );
            }

          }
          console.log(competitionsArray);
          return competitionsArray;
        })
      )
      .subscribe(competitions => {
          console.log(competitions);
          this.loadedCompetitions = competitions;
      });
  }
}
  

Я прикрепил снимок результата на моей консоли, который на самом деле не делает того, чего я действительно хочу достичь.

Снимок экрана с результатом кода

Комментарии:

1. Не могли бы вы, пожалуйста, включить код, в котором содержится ваш вызов фильтра?

2. Привет, Дервис, я включил его, вы можете посмотреть, когда будете свободны, спасибо за ваше время и помощь!

Ответ №1:

Я вижу здесь несколько проблем

  1. Вы пытаетесь получить асинхронную переменную this.loadedCompetitions синхронно. Это невозможно. Доступ ко всем асинхронным переменным должен осуществляться асинхронно. Вы могли бы использовать RxJS ReplaySubject multicast observable с буфером 1 для хранения и выдачи последнего значения.

  2. Вам не нужно вручную перебирать каждый элемент массива и создавать новый массив со _type === 'COMPETITION' свойством. Вы могли бы использовать функцию Array filter для фильтрации объектов на основе условия.

 ...
import { Observable, ReplaySubject } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable ({providedIn: 'root'})
export class CompetitionListService {
  private loadedCompetitions: ReplaySubject<Competition[]> = new ReplaySubject<Competition[]>(1);
  private url = '../../assets/data/program1.json';

  constructor(private http: HttpClient) {
    this.fetchCompetition();      // <-- triggers the request and pushes value to `loadedCompetitions`
  }

  public getCompetitions(): Observable<Competition[]> { 
    return this.loadedCompetitions.asObservable(); 
  }

  public fetchCompetition() {        // return nothing here
    this.http.get<Competition[]>(this.url).pipe(
      map(res => res.filter(item => item['_type'] !== 'COMPETITION'))
    ).subscribe(
      res => this.loadedCompetitions.next(res),
      err => console.log(err)           // <-- handle error
    );
  }
}
  
  1. Теперь вам нужно подписаться на loadedCompetitions переменную, чтобы получать от нее уведомления. Я использовал takeWhile оператор RxJS с Subject , чтобы закрыть все открытые подписки в ngOnDestroy привязке компонента.
 ...
import { Observable, Subject } from 'rxjs';
import { takeWhile } from 'rxjs/operators';

export class SomeComponent implements OnInit, OnDestroy {
  private close$ = new Subject<any>();    // <-- use to close open subscriptions

  constructor(private competitionListService: CompetitionListService) { }

  ngOnInit() {
    this.competitionListService.loadedCompetitions.pipe(
      takeWhile(this.close$)
    ).subscribe(
      res => {
        console.log(res);
        // other statements that depend on `res`
      }
    );
  }

  ngOnDestroy() {
    this.close$.next();     // <-- close open subscriptions
  }
}
  

Комментарии:

1. @UbunTube: Я не могу лично просмотреть ваши файлы. Попробуйте вышеупомянутое предложение, и если оно не сработает, вы можете попытаться найти решение, просмотрев другие ответы или опубликовав новый вопрос.