有没有更好的方法将子级别数组合成一个?

时间:2016-08-15 11:47:29

标签: javascript arrays

假设我在(简化的)艺术家阵列中拥有这些嵌套的专辑数组:

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.forEach((api) => {
    Object.keys(api).forEach((prop) => {
        if (prop === 'albums') {
            api[prop].forEach(album => albums.includes(album.title) ? '' : albums.push(album.title));
        }
    });
});

我想知道是否有更好,更简单的方法来实现这一目标,并且/或者如果在包含约5000位艺术家的数据集上这样做会很慢,每个艺术家有5-20张专辑

This answer被指出给我(可能重复),它确实解决了我的部分问题(非常有趣,谢谢!),但我对如何处理这个问题更感兴趣数组的嵌套特性。

谢谢!

3 个答案:

答案 0 :(得分:2)

我鼓励您通过函数式编程来探索lodash库来实现它。请参阅下面的代码,它是链接的,并且是声明性的,可读的,可维护的。

  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)

或检查http://jsbin.com/lisagemiqa/edit?js,console

答案 1 :(得分:1)

这是一个使用Set的解决方案,只需添加一次即可轻松完成:

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);

替代版本 - 相同的逻辑

通过查看这个替代的,更少函数式编程样式的代码来理解上面的代码可能会有所帮助,但它使用相同的逻辑:

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

由于此版本没有嵌套函数,因此您认为它运行得更快。但JavaScript引擎擅长优化代码,在我对FireFox和Chrome的测试中,第一个版本在大多数情况下运行速度稍快一些。

答案 2 :(得分: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), []
    );