Javascript : Filter a JSON object from API call to only get the infos I need

Viewed 677

I'm trying to create a small project to work on API calls. I have created an async that recovers infos about a track using the MusicBrainz API. You can check the result of the request by clicking there : https://musicbrainz.org/ws/2/recording/5935ec91-8124-42ff-937f-f31a20ffe58f?inc=genres+ratings+releases+artists&fmt=json (I chose Highway to Hell from AC/DC).

And here is what I got so far as reworking the JSON response of my request :

export const GET_JSON = async function (url) {
  try {
    const res = await Promise.race([
      fetch(url),
      timeout(CONSTANTS.TIMEOUT_SEC),
    ]);
    const data = await res.json();

    if (!res.ok) throw new Error(`${data.message} (${res.status})`);
    return data;
  } catch (err) {
    throw err;
  }
};
export const loadTrackDetail = async function (id) {
  try {
    const trackData = await GET_JSON(
      encodeURI(
        `${CONSTANTS.API_URL}${id}?inc=genres+artists+ratings+releases&fmt=json`
      )
    );
    details.trackDetails = {
      trackTitle: trackData.title,
      trackID: trackData.id,
      trackLength: trackData.length ?? "No duration provided",
      trackArtists: trackData["artist-credit"].length
        ? trackData["artist-credit"]
        : "No information on artists",

      trackReleases: trackData["releases"].length
        ? trackData["releases"]
        : "No information on releases",

      trackGenres: trackData["genres"].length
        ? trackData["genres"]
        : "No information on genres",
      trackRating: trackData.rating.value ?? "No rating yet",
    };

    console.log(details.trackDetails);
  } catch (err) {
    throw err;
  }

Now this isn't half bad, but the releases property for example is an array of objects (each one being a specific release on which the track is present) but for each of those releases, I want to "reduce" the object to its id and title only. The rest does not interest me. Moreover, I'd like to say that if, for example, the title of a release is similar to that of a previous one already present, the entire object is not added to the new array. I've thought about doing a foreach function, but I just can't wrap my head around how to write it correctly, if it's actually possible at all, if I should use an array.map for example, or another iterative method. If anyone has some nice way of doing this in pure JS (not Jquery !), efficient and clean, it'd be much appreciated !

Cheers

3 Answers

There are a few things that make this question a little difficult to answer, but I believe the below will get you pointed in the right direction.

  1. You don't include the GET_JSON method, so your example isn't complete and can't be used immediately to iterate on.
  2. In the example you bring, there isn't a name property on the objects contained in the releases array. I substituted name with title below to demonstrate the approach.
  3. You state

Moreover, I'd like to say that if, for example, the name of a release is similar to that of a previous one already present, the entire object is not added to the new array.

But you don't define what you consider that would make releases similar.

Given the above, as stated, I assumed you meant title when you said name and I also assumed that what would constitute a similar release would be one with the same name/title.

Assuming those assumptions are correct, I just fetch to retrieve the results. The response has a json method on it that will convert the response to a JSON object. The I map each release to the smaller data set you are interested in(id, title) and then reduce that array to remove 'duplicate' releases.

fetch('https://musicbrainz.org/ws/2/recording/5935ec91-8124-42ff-937f-f31a20ffe58f?inc=genres+ratings+releases+artists&fmt=json')
.then(m => m.json())
.then(j => {
  const reducedReleases = j.releases
                           .map(release => ({ id: release.id, name: release.title }))
                           .reduce(
                            (accumulator, currentValue, currentIndex, sourceArray) => {
                              if (!accumulator.find(a => a.name === currentValue.name)) {
                                accumulator.push(currentValue);
                              }
                              
                              return accumulator;
                            },
                            []);
  console.log(reducedReleases);
 });

const releasesReduced = []
const titleNotExist = (title) => {
    return releasesReduced.every(release => {
        if(release.title === title) return false;
        return true
    })
}
trackData["releases"].forEach(release => {
    if (titleNotExist(release.title))
        releasesReduced.push({id: release.id, title: release.title})
})
console.log(releasesReduced)

The array details.trackDetails.trackReleases has a path to an id and name from different objects. If you meant: ["release-events"]=>["area"]["id"]and["area"]["name"]` then see the demo below.

Demo uses flatMap() on each level of path to extract "release-events" then "area" to return an array of objects

[{name: area.name, id: area.id}, {name: area.name, id: area.id},...]

Then runs the array of pairs into a for...of loop and sets each unique name with id into a ES6 Map. Then it returns the Map as an object.

{name: id, name: id, ...}

To review this functioning, go to this Plunker

const releaseEvents = (details.trackDetails.trackReleases) => {
  let trackClone = JSON.parse(JSON.stringify(objArr));
  let areas = trackClone.flatMap(obj => {
    if (obj["release-events"]) {
      let countries = obj["release-events"].flatMap(o => {
        if (o["area"]) {
          let area = {};
          area.name = o["area"]["name"];
          area.id = o["area"]["id"];
          return [area];
        } else {
          return [];
        }
      });
      return countries;
    } else {
      return [];
    }
  });
  let eventAreas = new Map();
  for (let area of areas) {
    if (!eventAreas.has(area.name)) {
      eventAreas.set(area.name, area.id);
    }
  }
  return Object.fromEntries([...eventAreas]);
};

console.log(releaseEvents(releases));

Related