0

Let's say I have these nested arrays of albums in an (simplified) array of artists:

const api = [
    {
        "id": 1,
        "name": "John Coltrane",
        "albums": [
            { "title": "Giant Steps" },
            { "title": "A Love Supreme"},
            { "title": "Tenor Madness"}
        ],
        "instrument": "Saxophone"
    },
    {
        "id": 2,
        "name": "Sonny Rollins",
        "albums": [
            { "title": "Saxophone Colossus" },
            { "title": "The Bridge"},
            { "title": "Tenor Madness"}
        ],
        "instrument": "Saxophone"
    }
];

I want to combine all the albums into a new array, without any duplicates.

This is what I got so far:

let albums = [];
api.forEach((api) => {
    Object.keys(api).forEach((prop) => {
        if (prop === 'albums') {
            api[prop].forEach(album => albums.includes(album.title) ? '' : albums.push(album.title));
        }
    });
});

I'm wondering if there's a better, simpler, way to achieve this, and/or if this is going to be slow on a data set that includes about 5000 artists, with 5-20 albums each.

This answer was pointed out to me (possible duplicate), and it does address part of my question (and is very interesting, thanks!), but I'm more interested in how to deal with the nested nature of the array.

Thanks!

Community
  • 1
  • 1
Yann
  • 604
  • 2
  • 7
  • 16
  • 3
    If your question is really whether there is another way to something you are already doing, then this question is more suitable for asking on [CodeReview](http://codereview.stackexchange.com/) – trincot Aug 15 '16 at 11:53
  • Possible duplicate of [Remove Duplicates from JavaScript Array](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array) – Ilmari Karonen Aug 15 '16 at 12:04
  • I had no idea about CodeReview! Thanks for pointing it out to me @trincot – Yann Aug 15 '16 at 12:07

3 Answers3

2

I encourage you to explore lodash library to achieve it with functional programming. See the below code, it is chained, and it is declarative, readable, maintainable.

  const ret =  _(api)
    .map(x=>x.albums) // extract albums properties
    .flatten()        // flatten array of arrays to be array
    .map(x=>x.title)  // extract title
    .uniq()           // remove duplicacy
    .value();         // trigger evaluation process

  console.log(ret)

Or check http://jsbin.com/lisagemiqa/edit?js,console

Ron
  • 6,037
  • 4
  • 33
  • 52
1

Here is a solution making use of Set, which makes it easy to only add things once:

let albums = [...api.reduce((result, {albums}) => 
    albums.reduce((result, {title}) => result.add(title), result),
    new Set()
)];

const api = [
    {
        "id": 1,
        "name": "John Coltrane",
        "albums": [
            { "title": "Giant Steps" },
            { "title": "A Love Supreme"},
            { "title": "Tenor Madness"}
        ],
        "instrument": "Saxophone"
    },
    {
        "id": 2,
        "name": "Sonny Rollins",
        "albums": [
            { "title": "Saxophone Colossus" },
            { "title": "The Bridge"},
            { "title": "Tenor Madness"}
        ],
        "instrument": "Saxophone"
    }
];

let albums = [...api.reduce((result, {albums}) => 
    albums.reduce((result, {title}) => result.add(title), result),
    new Set()
)];

console.log(albums);

Alternative version -- same logic

It might be helpful to understand the above code, by looking at this alternative, less functional programming style of code, but which uses the same logic:

let result = new Set();
for (let row of api)
    for (let album of row.albums)
        result.add(album.title);
let albums = [...result];

As this version does not have nested functions, you'd think it runs faster. But JavaScript engines are good at optimising code, and in my tests on FireFox and Chrome, the first version even runs slightly faster most of the time.

trincot
  • 317,000
  • 35
  • 244
  • 286
0

ES2015:

const result = api
    .map(({albums}) => albums)
    .reduce((res, albums) => res.concat(albums), [])
    .reduce((res, {title}) => 
        (!res.some(saved => saved === title) && res.push(title), res), []
    );
Maxx
  • 1,740
  • 10
  • 17