JavaScript 对象合并问题

假设有这么一个对象:

{
   posts: [
        {
            title: "title1",
            url: "url1", // title 和 url 均不会重复
            tags: [{ name: "tagName1", slug: "tagSlug1" }], // tags 数组只有一个元素,下同
            ...
        },
        {
            title: "title2",
            url: "url2",
            tags: [{ name: "tagName2", slug: "tagSlug2" }],
            ...

        },
        {
            title: "title3",
            url: "url3",
            tags: [{ name: "tagName1", slug: "tagSlug1" }],
            ...

        },
        {
            title: "title4",
            url: "url4",
            tags: [{ name: "tagName1", slug: "tagSlug1" }],
            ...
        }
        ...
   ] 
}

我们可以看到,tags 数组是有重复的,试问如何才能合并具有相同 tags 数组的对象,同时将对应的不同 title 和 url 对象合并为数组,即,经过你的 js 处理后,生成类似下面的结构:

{
   posts: [
        {
            data: [ 
                { title: "title1", url: "url1" },
                { title: "title3", url: "url3" },
                { title: "title4", url: "url4" }
            ], 
            tags: [{name: "tagName1", slug: "tagSlug1"}],
            ...
        },
        {
            data: [
                { title: "title2", url: "url2" }
            ],
            tags: [{name: "tagName2", slug: "tagSlug2"}],
            ...
        },
        ...
   ] 
}

今天遇到的一个题目,想了很久也没有头绪,求老司机解答

阅读 2.4k
2 个回答

其实这里最麻烦是对 Object 判断,题主并没有给出 nameslug 是什么样的一个类型值,所以一切都变得非常难办。

但,也不是无从下手,可以借助第三方库,例如:lodash_.isEqual

let rv = data.posts.reduce((acc, val) => {
    let eItem = acc.find(w => _.isEqual(w.tags, val.tags));
    if (!eItem) {
        eItem = { title: [], tags: val.tags };
        acc.push(eItem);
    }
    
    eItem.title.push(val.title);
    
    return acc;
}, []);
const data = {
    posts: [
        {
            title: "title1",
            url: "url1", // title 和 url 均不会重复
            tags: [{ name: "tagName1", slug: "tagSlug1" }]
        },
        {
            title: "title2",
            url: "url2",
            tags: [{ name: "tagName2", slug: "tagSlug2" }]
        },
        {
            title: "title3",
            url: "url3",
            tags: [{ name: "tagName1", slug: "tagSlug1" }]
        },
        {
            title: "title4",
            url: "url4",
            tags: [{ name: "tagName1", slug: "tagSlug1" }]
        }
    ]
};

const result = (() => {
    // 简单的实现,复杂情况用 lodash
    function isEquals(a, b) {
        if (a === b) {
            return true;
        }

        if (typeof a === "string") {
            return false;
        }

        if (Array.isArray(a) && Array.isArray(b)) {
            return a.length === b.length
                ? a.every((item, i) => isEquals(item, b[i]))
                : false;
        }

        // 都当简单对象处理
        const ka = Object.keys(a);
        const kb = Object.keys(b);
        if (ka.length !== kb.length) {
            return false;
        }

        // 这里直接比值,如果是复杂的情况应该递归调用 isEquals
        return ka.every(key => a[key] === b[key]);
    }

    // 处理 data
    const posts = data.posts.reduce((r, post) => {
        let exists = r.find(t => isEquals(t.tags, post.tags));
        if (!exists) {
            exists = {
                data: [],
                tags: post.tags
            };
            r.push(exists);
        }
        exists.data.push({
            title: post.title,
            url: post.url
        });
        return r;
    }, []);

    return {
        posts: posts
    };
})();

function format(key, value) {
    switch (key) {
        case "tags":
            return JSON.stringify(value);
    }

    if (value.title && !value.tags) {
        return JSON.stringify(value);
    }

    return value;
}

console.log(JSON.stringify(data, format, 4));
console.log("[------------------------------------------------------------------------]");
console.log(JSON.stringify(result, format, 4));

输出

{
    "posts": [
        {
            "title": "title1",
            "url": "url1",
            "tags": "[{\"name\":\"tagName1\",\"slug\":\"tagSlug1\"}]"
        },
        {
            "title": "title2",
            "url": "url2",
            "tags": "[{\"name\":\"tagName2\",\"slug\":\"tagSlug2\"}]"
        },
        {
            "title": "title3",
            "url": "url3",
            "tags": "[{\"name\":\"tagName1\",\"slug\":\"tagSlug1\"}]"
        },
        {
            "title": "title4",
            "url": "url4",
            "tags": "[{\"name\":\"tagName1\",\"slug\":\"tagSlug1\"}]"
        }
    ]
}
[------------------------------------------------------------------------]
{
    "posts": [
        {
            "data": [
                "{\"title\":\"title1\",\"url\":\"url1\"}",
                "{\"title\":\"title3\",\"url\":\"url3\"}",
                "{\"title\":\"title4\",\"url\":\"url4\"}"
            ],
            "tags": "[{\"name\":\"tagName1\",\"slug\":\"tagSlug1\"}]"
        },
        {
            "data": [
                "{\"title\":\"title2\",\"url\":\"url2\"}"
            ],
            "tags": "[{\"name\":\"tagName2\",\"slug\":\"tagSlug2\"}]"
        }
    ]
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题