Reputation: 13
I have a JSON of music audio metadata with several tracks in a following structure:
var jsonObj = {
tracks: [
{ title: 'Another',
artist: 'Ataxia',
album: 'Automatic Writing',
year: '2004',
duration: 382
}]
};
And I want to transform it to have a following grouped structure:
var jsonObj = {
artists: [
{ name: 'Ataxia',
albums: [
{ name: 'Automatic Writing',
year: '2004',
tracks: [
{ title: 'Another',
duration: '382'
}]
}]
}]
};
Of course I tried doing it using pure JavaScript forEach() methods but it's a hell load of repetitive, immersive code and I'm looking for some smart solution. It could rely on some external Node.js package or JavaScript library.
Upvotes: 1
Views: 487
Reputation: 141
A simple way to combine artists and albums is to use dictionaries. Here's one way of processing the data through a dictionary, then generating the desired arrays once the tracks are organized by album and artist. Look in the console to see the results.
var jsonObj = {
tracks: [{
title: 'Another',
artist: 'Ataxia',
album: 'Automatic Writing',
year: '2004',
duration: 382
}]
};
var byArtist = {};
jsonObj.tracks.forEach(function(e) {
if (byArtist[e.artist] === undefined) {
// New artist, add to the dictionary
byArtist[e.artist] = {
artist: e.artist,
albums: {}
};
}
if (byArtist[e.artist].albums[e.album] == undefined) {
// New album, add to the dictionary
byArtist[e.artist].albums[e.album] = {
name: e.album,
year: e.year,
tracks: []
};
}
// Add the track
byArtist[e.artist].albums[e.album].tracks.push({
title: e.title,
duration: e.duration
});
});
// Convert the dictionaries to the final array structure
var result = {
artists: []
};
for (var artistKey in byArtist) {
if (byArtist.hasOwnProperty(artistKey)) {
var artist = {
name: byArtist[artistKey].artist,
albums: []
};
// We need to convert the album dictionary as well
for (var albumKey in byArtist[artistKey].albums) {
if (byArtist[artistKey].albums.hasOwnProperty(albumKey)) {
artist.albums.push(byArtist[artistKey].albums[albumKey]);
}
}
result.artists.push(artist);
}
}
console.log(result);
Upvotes: 1
Reputation: 708156
As I was working on putting it into the desired format, I had to search for an artist, search for an album, search for a track, etc... So, in thinking about it, I realized that you will probably want those functions yourself on the final data structure so rather than just write throw away code for converting one to the other, I made the resulting music list into an object that has various methods on it. One of those methods will add a list of tracks to the music list and have it organized as you've asked for.
So, here's that object and resulting demo. I add a couple more tracks just to better show the result. The end result here is you get an object that contains the music list and you get methods for finding an artist, album and track.
var trackList = {
tracks: [
{ title: 'Another',
artist: 'Ataxia',
album: 'Automatic Writing',
year: '2004',
duration: 382
},
{ title: 'Another2',
artist: 'Ataxia',
album: 'Automatic Writing',
year: '2007',
duration: 412
},
{ title: 'Another3',
artist: 'Ataxia3',
album: 'Automatic Writing',
year: '2008',
duration: 366
},
]
};
function Music() {
this.artists = [];
}
// helper function
// In an array of objects, it finds an object that has a specific property name
// with a specific value.
function findPropInArray(arr, propName, propValue) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][propName] === propValue) {
return arr[i];
}
}
return null;
}
Music.prototype = {
addTracks: function(list) {
var self = this;
list.forEach(function(t) {
self.addTrack(t);
});
},
addTrack: function(t) {
if (t.artist) {
var artistObj = this.findArtist(t.artist);
if (!artistObj) {
// if artist not found, then add the artist
artistObj = {name: t.artist, albums: []};
this.artists.push(artistObj);
}
var albumObj = this.findAlbum(artistObj, t.album);
if (!albumObj) {
// create album Obj
albumObj = {name: t.album, year: t.year, tracks: []};
artistObj.albums.push(albumObj);
}
if (!this.findTrack(albumObj, t.title)) {
albumObj.tracks.push({title: t.title, duration: t.duration});
}
}
},
findArtist: function(name) {
return findPropInArray(this.artists, "name", name);
},
findAlbum: function(artist, albumName) {
var artistObj = artist;
// if artist name was passed as a string, then find the object
if (typeof artist === "string") {
artistObj = this.findArtist(artist);
}
if (!artistObj) {
return null;
}
return findPropInArray(artistObj.albums, "name", albumName);
},
findTrack: function(albumObj, name) {
return findPropInArray(albumObj.tracks, "title", name);
}
}
var m = new Music();
m.addTracks(trackList.tracks);
var text = JSON.stringify(m, null, " ");
document.getElementById("result").textContent = text;
<pre id="result"></pre>
Upvotes: 0
Reputation: 3032
You can do something like the following (you will need to look at the console to see the results):
var jsonObj = {
tracks: [
{ title: 'Another',
artist: 'Ataxia',
album: 'Automatic Writing',
year: '2004',
duration: 382
}]
};
var jsonObjResult = {};
jsonObj['tracks'].forEach(function(track, index){
jsonObjResult['artists'] = [];
jsonObjResult['artists'][index] = {
'name' : track['artist']
, 'albums' : []
};
jsonObjResult['artists'][index]['albums'] = {
'name' : track['album']
, 'year' : track['year']
, 'tracks' : []
};
jsonObjResult['artists'][index]['albums']['tracks'] = {
'title' : track['title']
, 'duration' : track['duration']
};
});
console.log(jsonObjResult);
Upvotes: 0