Emman
Emman

Reputation: 4201

How to filter an object according to specification given in another object?

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

Answers (4)

trincot
trincot

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

ABDULLOKH MUKHAMMADJONOV
ABDULLOKH MUKHAMMADJONOV

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

ProDec
ProDec

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

Pran Paul
Pran Paul

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

Related Questions