Reputation: 4201
I want to filter out data from an object according to specification given in another object.
For example, consider the following BandAndAlbums
object that holds raw data about 3 bands (Beatles, Aerosmith, and Queen):
const BandAndAlbums = {
beatles: {
origin: 'liverpool',
genres: ['rock', 'pop', 'beat', 'psychedelia'],
labels: ['parlophone', 'apple', 'capitol'],
discography: {
yellowSubmarine: {
released: 1969,
length: 39.16,
producer: 'george martin',
label: 'apple',
},
rubberSoul: {
released: 1965,
length: 34.55,
producer: 'george martin',
label: 'parlophone',
},
letItBe: {
released: 1970,
length: 35.1,
producer: 'phil spector',
label: 'apple',
},
revolver: {
released: 1966,
length: 35.01,
producer: 'george martin',
label: 'parlophone',
},
},
},
aerosmith: {
origin: 'boston',
genres: [
'hard rock',
'blues rock',
'rock and roll',
'glam metal',
'heavy metal',
],
labels: ['columbia', 'geffen'],
discography: {
doneWithMirrors: {
released: 1985,
length: 35.42,
producer: 'ted templeman',
label: 'geffen',
},
rocks: {
released: 1976,
length: 34.31,
producer: 'jack douglas',
label: 'columbia',
},
nineLives: {
released: 1997,
length: 62.54,
producer: 'kevin shirley',
label: 'columbia',
},
},
},
queen: {
origin: 'london',
genres: ['rock'],
labels: [
'emi',
'parlophone',
'elektra',
'capitol',
'hollywood',
'island',
'virgin emi',
],
discography: {
aNightAtTheOpera: {
released: 1975,
length: 43.08,
producer: 'roy thomas baker',
label: 'emi',
},
newsOfTheWorld: {
released: 1977,
length: 39.1,
producer: 'queen',
label: 'emi',
},
aKindOfMagic: {
released: 1986,
length: 40.42,
producer: 'queen',
label: 'emi',
},
},
},
};
I want to filter BandAndAlbums
, to end with only chosen albums per band, and just the length
and producer
properties in each.
If I specify the albums that I want in the following requestedAlbums
:
const requestedAlbums = {
queen: ['aNightAtTheOpera', 'aKindOfMagic'],
beatles: ['rubberSoul', 'letItBe', 'revolver'],
aerosmith: ['doneWithMirrors', 'nineLives'],
};
how can I filter BandAndAlbums
against it, and create a new object, with just length
and producer
per chosen album?
desired output
const desiredOutput = {
beatles: {
rubberSoul: {
length: 34.55,
producer: 'george martin',
},
letItBe: {
length: 35.1,
producer: 'phil spector',
},
revolver: {
length: 35.01,
producer: 'george martin',
},
},
aerosmith: {
doneWithMirrors: {
length: 35.42,
producer: 'ted templeman',
},
nineLives: {
length: 62.54,
producer: 'kevin shirley',
},
},
queen: {
aNightAtTheOpera: {
length: 43.08,
producer: 'roy thomas baker',
},
aKindOfMagic: {
length: 40.42,
producer: 'queen',
},
},
};
EDIT
To address the comments below, I shall add that I am familiar with the procedure of converting an object into an array (with Object.entries()
) thus promoting the usage of array methods (e.g., .filter()
, .map()
, etc.), and finally converting back to an object with Object.fromEntries()
.
Unfortunately, in this case I'm a bit lost with how to do what I need. I assume that in order to jumpstart, I need to do something along the lines of:
Object.entries(BandAndAlbums).map( ([k, v]) => [k, v.discography])
(But then I get this undefined
)
// [ [ 'queen', undefined ],
// [ 'beatles', undefined ],
// [ 'aerosmith', undefined ] ]
So I'll be grateful is someone could steer me to the right direction. Thanks.
Upvotes: 1
Views: 78
Reputation: 350365
You can use Object.entries
to get an array of pairs, then do all the mapping and filtering, and finally turn the resulting array of pairs back to the result object with Object.fromEntries
.
Object.fromEntries
can also be applied at a nested level to produce the nested objects:
const BandAndAlbums = {beatles: {origin: 'liverpool',genres: ['rock', 'pop', 'beat', 'psychedelia'],labels: ['parlophone', 'apple', 'capitol'],discography: {yellowSubmarine: {released: 1969,length: 39.16,producer: 'george martin',label: 'apple',},rubberSoul: {released: 1965,length: 34.55,producer: 'george martin',label: 'parlophone',},letItBe: {released: 1970,length: 35.1,producer: 'phil spector',label: 'apple',},revolver: {released: 1966,length: 35.01,producer: 'george martin',label: 'parlophone',},},},aerosmith: {origin: 'boston',genres: ['hard rock','blues rock','rock and roll','glam metal','heavy metal',],labels: ['columbia', 'geffen'],discography: {doneWithMirrors: {released: 1985,length: 35.42,producer: 'ted templeman',label: 'geffen',},rocks: {released: 1976,length: 34.31,producer: 'jack douglas',label: 'columbia',},nineLives: {released: 1997,length: 62.54,producer: 'kevin shirley',label: 'columbia',},},},queen: {origin: 'london',genres: ['rock'],labels: ['emi','parlophone','elektra','capitol','hollywood','island','virgin emi',],discography: {aNightAtTheOpera: {released: 1975,length: 43.08,producer: 'roy thomas baker',label: 'emi',},newsOfTheWorld: {released: 1977,length: 39.1,producer: 'queen',label: 'emi',},aKindOfMagic: {released: 1986,length: 40.42,producer: 'queen',label: 'emi',},},},};
const requestedAlbums = {queen: ['aNightAtTheOpera', 'aKindOfMagic'],beatles: ['rubberSoul', 'letItBe', 'revolver'],aerosmith: ['doneWithMirrors', 'nineLives'],};
const output = Object.fromEntries(
Object.entries(requestedAlbums).map(([band, albums]) => [
band,
Object.fromEntries(
albums.map(album => [album, BandAndAlbums[band].discography[album]])
.map(([album, {length, producer}]) => [album, {length, producer}])
)
])
);
console.log(output);
Upvotes: 1
Reputation: 5234
You can achieve this easily with the combination of Array.reduce()
and Array.entries()
:
const BandAndAlbums = {
beatles: {
origin: 'liverpool',
genres: ['rock', 'pop', 'beat', 'psychedelia'],
labels: ['parlophone', 'apple', 'capitol'],
discography: {
yellowSubmarine: {
released: 1969,
length: 39.16,
producer: 'george martin',
label: 'apple',
},
rubberSoul: {
released: 1965,
length: 34.55,
producer: 'george martin',
label: 'parlophone',
},
letItBe: {
released: 1970,
length: 35.1,
producer: 'phil spector',
label: 'apple',
},
revolver: {
released: 1966,
length: 35.01,
producer: 'george martin',
label: 'parlophone',
},
},
},
aerosmith: {
origin: 'boston',
genres: [
'hard rock',
'blues rock',
'rock and roll',
'glam metal',
'heavy metal',
],
labels: ['columbia', 'geffen'],
discography: {
doneWithMirrors: {
released: 1985,
length: 35.42,
producer: 'ted templeman',
label: 'geffen',
},
rocks: {
released: 1976,
length: 34.31,
producer: 'jack douglas',
label: 'columbia',
},
nineLives: {
released: 1997,
length: 62.54,
producer: 'kevin shirley',
label: 'columbia',
},
},
},
queen: {
origin: 'london',
genres: ['rock'],
labels: [
'emi',
'parlophone',
'elektra',
'capitol',
'hollywood',
'island',
'virgin emi',
],
discography: {
aNightAtTheOpera: {
released: 1975,
length: 43.08,
producer: 'roy thomas baker',
label: 'emi',
},
newsOfTheWorld: {
released: 1977,
length: 39.1,
producer: 'queen',
label: 'emi',
},
aKindOfMagic: {
released: 1986,
length: 40.42,
producer: 'queen',
label: 'emi',
},
},
},
};
const requestedAlbums = {
queen: ['aNightAtTheOpera', 'aKindOfMagic'],
beatles: ['rubberSoul', 'letItBe', 'revolver'],
aerosmith: ['doneWithMirrors', 'nineLives'],
};
const result2 = Object.entries(requestedAlbums).reduce((tot, [k, v]) => {
// all songs of the band
const allSongs = BandAndAlbums[k].discography
// take chosen ones
const chosenSongs = v.reduce((chosen, song) => {
chosen[song] = {
length: allSongs[song].length,
producer: allSongs[song].producer
}
return chosen
}, {})
// add to total
tot[k] = chosenSongs
return tot
}, {})
console.log(result2)
Upvotes: 1
Reputation: 5410
This could work, utilizing Object & Array functions.
const filter = (BandAndAlbums, requestedAlbums) => Object.fromEntries(
Object.entries(requestedAlbums).map(([name, list]) => [name, Object.fromEntries(
list.map(entry => [entry, BandAndAlbums[name]?.discography[entry]])
.filter(([entry, discography]) => discography)
.map(([entry, {length, producer}]) => [entry, {length, producer}]
))]
)
);
const BandAndAlbums = {
beatles: {
origin: 'liverpool',
genres: ['rock', 'pop', 'beat', 'psychedelia'],
labels: ['parlophone', 'apple', 'capitol'],
discography: {
yellowSubmarine: {
released: 1969,
length: 39.16,
producer: 'george martin',
label: 'apple',
},
rubberSoul: {
released: 1965,
length: 34.55,
producer: 'george martin',
label: 'parlophone',
},
letItBe: {
released: 1970,
length: 35.1,
producer: 'phil spector',
label: 'apple',
},
revolver: {
released: 1966,
length: 35.01,
producer: 'george martin',
label: 'parlophone',
},
},
},
aerosmith: {
origin: 'boston',
genres: [
'hard rock',
'blues rock',
'rock and roll',
'glam metal',
'heavy metal',
],
labels: ['columbia', 'geffen'],
discography: {
doneWithMirrors: {
released: 1985,
length: 35.42,
producer: 'ted templeman',
label: 'geffen',
},
rocks: {
released: 1976,
length: 34.31,
producer: 'jack douglas',
label: 'columbia',
},
nineLives: {
released: 1997,
length: 62.54,
producer: 'kevin shirley',
label: 'columbia',
},
},
},
queen: {
origin: 'london',
genres: ['rock'],
labels: [
'emi',
'parlophone',
'elektra',
'capitol',
'hollywood',
'island',
'virgin emi',
],
discography: {
aNightAtTheOpera: {
released: 1975,
length: 43.08,
producer: 'roy thomas baker',
label: 'emi',
},
newsOfTheWorld: {
released: 1977,
length: 39.1,
producer: 'queen',
label: 'emi',
},
aKindOfMagic: {
released: 1986,
length: 40.42,
producer: 'queen',
label: 'emi',
},
},
},
};
const requestedAlbums = {
queen: ['aNightAtTheOpera', 'aKindOfMagic'],
beatles: ['rubberSoul', 'letItBe', 'revolver'],
aerosmith: ['doneWithMirrors', 'nineLives'],
};
console.log(filter(BandAndAlbums, requestedAlbums));
Upvotes: 1
Reputation: 68
I think below function will solve your requirement:
const BandAndAlbums = {
beatles: {
origin: 'liverpool',
genres: ['rock', 'pop', 'beat', 'psychedelia'],
labels: ['parlophone', 'apple', 'capitol'],
discography: {
yellowSubmarine: {
released: 1969,
length: 39.16,
producer: 'george martin',
label: 'apple',
},
rubberSoul: {
released: 1965,
length: 34.55,
producer: 'george martin',
label: 'parlophone',
},
letItBe: {
released: 1970,
length: 35.1,
producer: 'phil spector',
label: 'apple',
},
revolver: {
released: 1966,
length: 35.01,
producer: 'george martin',
label: 'parlophone',
},
},
},
aerosmith: {
origin: 'boston',
genres: [
'hard rock',
'blues rock',
'rock and roll',
'glam metal',
'heavy metal',
],
labels: ['columbia', 'geffen'],
discography: {
doneWithMirrors: {
released: 1985,
length: 35.42,
producer: 'ted templeman',
label: 'geffen',
},
rocks: {
released: 1976,
length: 34.31,
producer: 'jack douglas',
label: 'columbia',
},
nineLives: {
released: 1997,
length: 62.54,
producer: 'kevin shirley',
label: 'columbia',
},
},
},
queen: {
origin: 'london',
genres: ['rock'],
labels: [
'emi',
'parlophone',
'elektra',
'capitol',
'hollywood',
'island',
'virgin emi',
],
discography: {
aNightAtTheOpera: {
released: 1975,
length: 43.08,
producer: 'roy thomas baker',
label: 'emi',
},
newsOfTheWorld: {
released: 1977,
length: 39.1,
producer: 'queen',
label: 'emi',
},
aKindOfMagic: {
released: 1986,
length: 40.42,
producer: 'queen',
label: 'emi',
},
},
},
};
const requestedAlbums = {
queen: ['aNightAtTheOpera', 'aKindOfMagic'],
beatles: ['rubberSoul', 'letItBe', 'revolver'],
aerosmith: ['doneWithMirrors', 'nineLives'],
};
// This is your desired funciton
var filterAlbum=(a,r)=>{
var ou = {};
for(let ri in r){
if(typeof a[ri]!== 'undefined'){
var our = {};
for(let di of r[ri]){
if(typeof a[ri].discography[di]!=='undefined'){
let dis = a[ri].discography[di];
our[di]={length:dis.length,producer:dis.producer};
}
}
ou[ri] = our;
}
}
return ou;
}
const desiredOutput = filterAlbum(BandAndAlbums,requestedAlbums);
console.log(desiredOutput);
You can ask more if you need more filters here. Thanks
Upvotes: 1