group and filter JSON file

You can reduce() the array into a Map which makes accumulating duplicates straightforward. You can also just have a running tally of listeners thus avoiding a second iteration.

The result is a Map of the shape:

{
  "Artist1": {
    "songs": [
      {...},
      {...}
    ]
    "listeners": 0
  },
  "Artist2": {
    "songs": [
      {...},
      {...}
    ]
    "listeners": 0
  }
  ...
}

const songs = [
    {
        "name": "The Less I Know the Better",
        "duration": "0",
        "listeners": "439958",
        "mbid": "",
        "url": "https://www.last.fm/music/Tame+Impala/_/The+Less+I+Know+the+Better",
        "artist": {
            "name": "Tame Impala",
            "mbid": "63aa26c3-d59b-4da4-84ac-716b54f1ef4d",
            "url": "https://www.last.fm/music/Tame+Impala"
        },
        "@attr": {
            "rank": "0"
        },
        "genre": "reggae"
    },
    {
        "name": "Creep",
        "duration": "239",
        "listeners": "1647583",
        "mbid": "d11fcceb-dfc5-4d19-b45d-f4e8f6d3eaa6",
        "url": "https://www.last.fm/music/Radiohead/_/Creep",
        "artist": {
            "name": "Radiohead",
            "mbid": "a74b1b7f-71a5-4011-9441-d0b5e4122711",
            "url": "https://www.last.fm/music/Radiohead"
        },
        "@attr": {
            "rank": "1"
        },
        "genre": "jazz"
    },
    {
        "name": "Go to Sleep",
        "duration": "239",
        "listeners": "16583",
        "mbid": "d11fcceb-dfc5-4d19-b45d-f4e8f6d3eaa6",
        "url": "https://www.last.fm/music/Radiohead/_/Go to Sleep",
        "artist": {
            "name": "Radiohead",
            "mbid": "a74b1b7f-71a5-4011-9441-d0b5e4122711",
            "url": "https://www.last.fm/music/Radiohead"
        },
        "@attr": {
            "rank": "3"
        },
        "genre": "jazz"
    }
]

const byArtist = songs.reduce((acc, song) => {
  const artistName = song.artist.name;
  const match = acc.get(artistName);
  if (match) {
    match.songs.push({...song});
    match.listeners += parseInt(song.listeners);
  } else {
    acc.set(artistName, {songs: [{...song}], listeners: +song.listeners});
  }
  return acc;
}, new Map);

console.log(Object.fromEntries(byArtist));

Explanation

You can read up on Map and reduce() for further insight.

const byArtist = songs.reduce((acc, song) => {
  // get the artist name from the current song object
  const artistName = song.artist.name;
  // try to get the element referenced by artistName,
  // returns undefined if we haven't added that artist yet
  // otherwise it returns the object holding "songs" and "listeners"
  const match = acc.get(artistName);
  if (match) {
    // if match is truthy (successfully retrieved an entry)
    // push a copy of the song into the "songs" array
    match.songs.push({...song});
    // increment the listener count by the songs listeners
    match.listeners += parseInt(song.listeners);
  } else {
    // else set a new key: value pair useing artistName as the key
    // and creating a new object contianing "songs" and "listeners"
    acc.set(artistName, {songs: [{...song}], listeners: +song.listeners});
  }
  // return the mutated accumulator for the next iteration to use.
  return acc;
}, new Map); // this initializes the accumulator as a new Map object

// creates an Object from the Map so that it can be logged easily
console.log(Object.fromEntries(byArtist));

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top