Search code examples
javascriptnode.jslodash

JavaScript array of objects grouping based on key and value


let data =  [
{news: id1, users: [{user1}, {user2}, {user3}]},
{news: id2, users: [{user2}, {user4}, {user6}]},
{news: id3, users: [{user1}, {user2}, {user4}]}
]

So from the above data, I need to be able to group news items to form a template(let's consider a template consists strictly two news items). Then for every template, I need to match the corresponding user. Finally, when I'm done with creating all the templates I need to map remaining users to their respective news items.

I've added a sample output below

[id1,id2] : [user2]
[id2, id3] : [user2, user4]
[id3, id1] : [user1, user2]
[id2] : [user6]
[id1] : [user3]

How do I achieve this?


Solution

  • You could take a hash table and build the wanted structure by checking the length of the values and if greater than 2 take the combination of the values for adding the new grouups to the result set, instead of the given group.

    var data = [{ news: 'id1', users: ['user1', 'user2', 'user3'] }, { news: 'id2', users: ['user2', 'user4', 'user6'] }, { news: 'id3', users: ['user1', 'user2', 'user4'] }],
        cool = data.reduce((r, { news, users }) => users.reduce((o, user) => ((o[user] = o[user] || []).push(news), o), r), {}),
        result = Object.entries(cool).reduce((r, [user, news]) => {
            function add(k, v) {
                var temp = r.find(([a]) => a.join('|') === v.join('|'));
                if (!temp) r.push(temp = [v, []]);
                temp[1].push(k);
            }
    
            if (news.length > 2) {
                news.reduce((r, v, i, a) => r.concat(a.slice(i + 1).map(w => [v, w])), [])
                    .forEach(news => add(user, news));
            } else {
                add(user, news);
            }
            return r;
        }, []);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }