javascriptarraysnested-loopssymmetry

Get consolidated difference between arrays - from the need to sync arrays in this situation


I'm not sure if I've described the problem best in the title but I'll elaborate here.

My overall goal is to keep lists in sync, I'm currently trying to get a specific output so that I can later correct list symmetry.

I've figured it out to this point:

Code:

let list2 = [
{user: 001, log: [1,2,3,4,5,6,7,8,9,10]},
{user: 002, log: [2,3,4,5,6,7,8,9, 44]},
{user: 003, log: [1,2,3,4,6,7,8]},
{user: 004, log: [1,2,3,4,5,6,7,8]}
];

for (let comparator = 0; comparator < list2.length; comparator++) {
    for (let index = 0; index < list2.length; index++) {
        if (comparator !== index) {
            let currentDiff = list2[comparator].log.filter(x => !list2[index].log.includes(x));
                    console.log("User: " + list2[index].user + " needs " + currentDiff + " from user: " + list2[comparator].user);
    }
  }
}

Output:

User: 2 needs 1,10 from user: 1
User: 3 needs 5,9,10 from user: 1
User: 4 needs 9,10 from user: 1
User: 1 needs 44 from user: 2
User: 3 needs 5,9,44 from user: 2
User: 4 needs 9,44 from user: 2
User: 1 needs  from user: 3
User: 2 needs 1 from user: 3
User: 4 needs  from user: 3
User: 1 needs  from user: 4
User: 2 needs 1 from user: 4
User: 3 needs 5 from user: 4

This outputs way too much data & I want to condense it

Desired output is all data is condensed so that none of the "needs" repeats, so if for example, user #2 can get 1 & 10 from user #1, then there is no need to output user #2 needs 1 from user #3... Do you follow me? I think this could be made simple but I just don't know any actions that can get this done easily.

Here is what I'm trying to achieve as output model (ideally):

[
{"user": 1,
"symmetriseLogs": [
                   {user: 2, missingLogs: [1, 10]},
                   {user: 3, missingLogs: [5, 9, 10]},
                   {user: 4, missingLogs: [9, 10]},
                                                       ]},
{"user": 2,
"symmetriseLogs": [
                   {user: 1, missingLogs: [44]},
                   {user: 3, missingLogs: [44]},
                   {user: 4, missingLogs: [44]},
                                                       ]},
]

Output should be whats required to symmetrize all the logs, so in the example output everything missing with user #1 & #2 can be gotten from each other, thus user #3 & #4 do not get output. Also user #2 only requires outputting 44 as that is the only log item 44 has that others are missing and which can't be gotten from user #1.

Bit of a looping logic nightmare, I would appreciate any assistance solving this. I've only gotten more confusing outputs trying to achieve this.


Solution

  • One approach is, before starting the iteration, you can make a mirrored structure that maps each user to the logs it has so far. Inside the loop, look up the user's existing logs to check to see which numbers need to be added.

    It's not as pure as .map is intended to be, but it does the job, and I can't think of a nicer-looking approach.

    const list2 = [
    {user: 001, log: [1,2,3,4,5,6,7,8,9,10]},
    {user: 002, log: [2,3,4,5,6,7,8,9, 44]},
    {user: 003, log: [1,2,3,4,6,7,8]},
    {user: 004, log: [1,2,3,4,5,6,7,8]}
    ];
    
    const haveLogsByUserId = new Map(list2.map(({ user, log }) => [user, new Set(log)]));
    
    const result = list2.map((source, i) => ({
      user: source.user,
      symmetriseLogs: list2
        .filter((_, j) => i !== j)
        .map(dest => {
          const thisUserLogs = haveLogsByUserId.get(dest.user);
          const missingLogs = source.log.filter(num => !thisUserLogs.has(num));
          for (const num of missingLogs) thisUserLogs.add(num);
          return { user: dest.user, missingLogs };
        })
        .filter(missingObj => missingObj.missingLogs.length)
    }));
    console.log(result);