Winthorpe
Winthorpe

Reputation: 1200

Use rxjs/Typescript to map an array inside a response from an external API

I am new to Typescript/Javascript and I am trying to get to the grips with the potential of rxjs. I use this code

return this.http.get<IXenoCantoResponse>(`${this.corsAnywhereUrl}${this.xenoCantoApiBaseUrl}${this.formatSearchTerm(searchTerm)}${this.recordingLength}`)
  .pipe(
    map(o => ({
      numRecordings: o.numRecordings,
      numSpecies: o.numSpecies,
      page: o.page,
      numPages: o.numPages,
      recordings: o.recordings
    }))
  );

to map the response from a public API to an interface, IXenoCanto:

export interface IXenoCantoResponse {
    numRecordings: string;
    numSpecies: string;
    page: string;
    numPages: string;
    recordings: [];
  }

It is actually the recordings[] that I am interested in. In subsequent code, I use the fields in this array to construct an url and add it to yet another object:

data.forEach((element, index) => {
  let sub = element.sono['full'].substr(0, this.getPosition(element.sono['full'], '\/', 6));
  urls.push({
    id: index + 1,
    url: `${sub}${element['file-name']}`
  });

});

export interface IVoice {
  id: number;
  url: string;
}

While it is true that this code does the job, I have been attempting to make it more effiently.

I wonder if it is possible to map the recordings any[] response straight to a IVoice[] in my initial map code. So I have tried changing the IXenoCantoResponse interface like so:

export interface IXenoCantoResponse {
    numRecordings: string;
    numSpecies: string;
    page: string;
    numPages: string;
    recordings: IVoice[]
    // recordings: [];
  }

And then I can try to map the [] repsonse to the IVoice[] directly, something like this:

return this.http.get<IXenoCantoResponse>(`${this.corsAnywhereUrl}${this.xenoCantoApiBaseUrl}${this.formatSearchTerm(searchTerm)}${this.recordingLength}`)
      .pipe(
        map(o => ({
          numRecordings: o.numRecordings,
          numSpecies: o.numSpecies,
          page: o.page,
          numPages: o.numPages,
          recordings: o.recordings.map((element, index) => {

              id: index + 1,
              url: `${element.sono['full'].substr(0, this.getPosition(element.sono['full'], '\/', 6))}${element['file-name']}`
          }
          });
        }))
      );

I have so far been unable to get the second approach to work. Is it possible to do this? I think the problem lies in how to setup the map operator with an array object. Is this right and can anyone point me in the right direction?

Edit:

Each object in the recordings array mentioned above has the following format:

        {
            "id": "477551",
            "gen": "Troglodytes",
            "sp": "troglodytes",
            "ssp": "troglodytes",
            "en": "Eurasian Wren",
            "rec": "\u00c9tienne Leroy",
            "cnt": "Poland",
            "loc": "Hajn\u00f3wka, hajnowski, podlaskie",
            "lat": "52.6907",
            "lng": "23.6035",
            "alt": "160",
            "type": "song",
            "url": "\/\/www.xeno-canto.org\/477551",
            "file": "\/\/www.xeno-canto.org\/477551\/download",
            "file-name": "XC477551-190503-Troglodyte [email protected]",
            "sono": {
                "small": "\/\/www.xeno-canto.org\/sounds\/uploaded\/ZWAQHOJFLZ\/ffts\/XC477551-small.png",
                "med": "\/\/www.xeno-canto.org\/sounds\/uploaded\/ZWAQHOJFLZ\/ffts\/XC477551-med.png",
                "large": "\/\/www.xeno-canto.org\/sounds\/uploaded\/ZWAQHOJFLZ\/ffts\/XC477551-large.png",
                "full": "\/\/www.xeno-canto.org\/sounds\/uploaded\/ZWAQHOJFLZ\/ffts\/XC477551-full.png"
            },
            "lic": "\/\/creativecommons.org\/licenses\/by-nc-sa\/4.0\/",
            "q": "A",
            "length": "1:13",
            "time": "08:00",
            "date": "2019-05-03",
            "uploaded": "2019-05-29",
            "also": [
                "Fringilla coelebs"
            ],
            "rmk": "Singing seated or in flight",
            "bird-seen": "yes",
            "playback-used": "no"
        }

Upvotes: 0

Views: 69

Answers (1)

AlleXyS
AlleXyS

Reputation: 2598

I think you need to specify the type of created objects

return this.http.get<IXenoCantoResponse>(`${this.corsAnywhereUrl}${this.xenoCantoApiBaseUrl}${this.formatSearchTerm(searchTerm)}${this.recordingLength}`)
   .pipe(
      map(o => return new IXenoCantoResponse({
          numRecordings: o.numRecordings,
          numSpecies: o.numSpecies,
          page: o.page,
          numPages: o.numPages,
          recordings: o.recordings.map((element: IVoice, index) => new IVoice({
              id: index + 1,
              url: `${element['sono']['full'].substr(0, this.getPosition(element['sono']['full'], '\/', 6))}${element['file-name']}`
          })
        });
      }))
    );

Upvotes: 1

Related Questions