'Add another validation to avoid joining the array

I am matchmaking system where 2 players will be matched if they have the same weight. I have my old codes and my new codes. My old codes are working but I have made an adjustment so i improved and changed my code. Now, my target is the same just like my old code.

For this old code, we can see that there's a "noFight" in the array. nofight means, this player should not be matched with the given entryID. For example:

entryID: 1 has a noFight: [2,3], it means that entryID 1 should not be matched with entryID 2 and 3.

const source = [
  {
    entryID: 1,
    entryName: "player1",
    weight: 1900,
    noFight: [2,3], // changed these to arrays
  },
  {
    entryID: 2,
    entryName: "player2",
    weight: 1900,
   noFight: [1,3,4],
  },
  {
    entryID: 3,
    entryName: "player3",
    weight: 1900,
    noFight: [1,2,4],
  },
    {
    entryID: 4,
    entryName: "player4",
    weight: 1900,
    noFight: [2,3,4],
  },
    { // added a new player that will fight anyone to test the logic
    entryID: 5,
    entryName: "player5",
    weight: 1900,
    noFight: [],
  },
];

function combine(
  data = [],
  different = 0,
  maxGroupSize = 2,
  sortedStatement = (a, b) => a.weight - b.weight
) {
  const sortedData = [...data].sort(sortedStatement);

  const dataGroups = sortedData.reduce((acc, item) => {
    const findedAccItem = acc.find(
      (accItem) =>
        accItem.length < maxGroupSize &&
        accItem[0].weight + different >= item.weight &&
        !accItem.find((obj) => obj.entryName === item.entryName || item.noFight.includes(obj.entryID)) // here we check if the fighter is in the noFight array or not. I need to add this item.noFight.includes(obj.entryID) validation to my new codes.
    );
    
    if (findedAccItem) {
      findedAccItem.push(item);
    } else {
      acc.push([item]);
    }
    
    return acc;
  }, []);

  const namedDataGroups = dataGroups.reduce((acc, item, index) => {
    if (item.length > 1) { // and finally we check that there are in fact two fighters in the grouping; without this you'll end up with  fighters that didn't get a match in the array as a result of how the reduce above works
      const key = [index, ...item.map((item) => item.weight)].join("_");
      acc[key] = item;
    }
    return acc;
  }, {});

  return namedDataGroups;
}

console.log("Example #1: ", combine(source));

Now in this new code. I cannot achieve my target here. The nofights.

 const source = [
      {
        entryID: 1,
        entryName: "player1",
        weight: 1900,
        noFight: [2,3], // changed these to arrays
      },
      {
        entryID: 2,
        entryName: "player2",
        weight: 1900,
       noFight: [1,3,4],
      },
      {
        entryID: 3,
        entryName: "player3",
        weight: 1900,
        noFight: [1,2,4],
      },
        {
        entryID: 4,
        entryName: "player4",
        weight: 1900,
        noFight: [2,3,4],
      },
        { // added a new player that will fight anyone to test the logic
        entryID: 5,
        entryName: "player5",
        weight: 1900,
        noFight: [],
      },
    ];
  
console.log ( combine(source) )

function combine( data = [], different = 0, maxGroupSize = 2 ) 
  {
  const 
    groups        = []
  , related       = []
  , sortedData    = [...data].sort((a, b) => a.weight - b.weight)
  , alreadyInRela = (setX,eName) =>
    {
    let list = [...setX, eName] 
    return related.some(rela=>list.every(l=>rela.has(l)))
    };
  sortedData.forEach((el,indx)=>
    {
    let place = groups.findIndex( // find a place in a group forEach element, use indx as track
        g => g.names.size < maxGroupSize              // is the group incomplete ?
            && !g.names.has(el.entryName)             // is entryName not in the group list (names Set) ?
            && (el.weight - g.weight) <= different    // is the weight falls within the weight range ?
            && !alreadyInRela(g.names, el.entryName ) // is (entryName + group list) does not already used ?
        )
    if (place < 0) // not found -> create new group
      {
      let names = new Set().add(el.entryName)                      // create new group
      groups.push( { names, indxs: [indx], weight: el.weight } )  // group constitutive info 
      related.push( names )                                      // keep track of group list
      }
    else // find a place in a group
      {
      groups[place].names.add(el.entryName)  // related list is also updated
      groups[place].indxs.push(indx)        // add indx to retreive element in sortedData 
      }
    })
  return groups.reduce((r,g,i)=> // build result
    {
    let key = `${i}_` + g.indxs.map(x=>sortedData[x].weight).join('_')
    r[key] = []
    g.indxs.forEach(x=> r[key].push( sortedData[x]) )
    return r
    }, {} )
  }

Output of my current codes:

{
  "0_1900_1900": [
    {
      "entryID": 1,
      "entryName": "player1",
      "weight": 1900,
      "noFight": [
        2,
        3
      ]
    },
    {
      "entryID": 2,
      "entryName": "player2",
      "weight": 1900,
      "noFight": [
        1,
        3,
        4
      ]
    }
  ],
  "1_1900_1900": [
    {
      "entryID": 3,
      "entryName": "player3",
      "weight": 1900,
      "noFight": [
        1,
        2,
        4
      ]
    },
    {
      "entryID": 4,
      "entryName": "player4",
      "weight": 1900,
      "noFight": [
        2,
        3,
        4
      ]
    }
  ],
  "2_1900": [
    {
      "entryID": 5,
      "entryName": "player5",
      "weight": 1900,
      "noFight": []
    }
  ]
}

My target output:

 {
  "0_1900_1900": [
    {
      "entryID": 1,
      "entryName": "player1",
      "weight": 1900,
      "noFight": [
        2,
        3
      ]
    },
    {
      "entryID": 4,
      "entryName": "player4",
      "weight": 1900,
      "noFight": [
        2,
        3,
        4
      ]
    }
  ],
  "1_1900_1900": [
    {
      "entryID": 2,
      "entryName": "player2",
      "weight": 1900,
      "noFight": [
        1,
        3,
        4
      ]
    },
    {
      "entryID": 5,
      "entryName": "player5",
      "weight": 1900,
      "noFight": []
    }
  ]
}


Solution 1:[1]

Your new code is almost identical to old except 2 things:

  1. noFight isn't checked in new code.
  2. All groups are used to build result in new code. In old code, only groups of size > 1 are used.

I've made the changes in following snippet.

const source = [
  {
    entryID: 1,
    entryName: "player1",
    weight: 1900,
    noFight: [2,3], // changed these to arrays
  },
  {
    entryID: 2,
    entryName: "player2",
    weight: 1900,
   noFight: [1,3,4],
  },
  {
    entryID: 3,
    entryName: "player3",
    weight: 1900,
    noFight: [1,2,4],
  },
    {
    entryID: 4,
    entryName: "player4",
    weight: 1900,
    noFight: [2,3,4],
  },
    { // added a new player that will fight anyone to test the logic
    entryID: 5,
    entryName: "player5",
    weight: 1900,
    noFight: [],
  },
];

console.log ( combine(source) )

function combine( data = [], different = 0, maxGroupSize = 2 ) 
{
const 
groups        = []
, related       = []
, sortedData    = [...data].sort((a, b) => a.weight - b.weight)
, alreadyInRela = (setX,eName) =>
{
let list = [...setX, eName] 
return related.some(rela=>list.every(l=>rela.has(l)))
};
sortedData.forEach((el,indx)=>
{
let place = groups.findIndex( // find a place in a group forEach element, use indx as track
    g => g.names.size < maxGroupSize              // is the group incomplete ?
        && !g.names.has(el.entryName)             // is entryName not in the group list (names Set) ?
        && (el.weight - g.weight) <= different    // is the weight falls within the weight range ?
        && !alreadyInRela(g.names, el.entryName ) // is (entryName + group list) does not already used ?
        && !el.noFight.some(noFightEl => g.indxs.map(gi => sortedData[gi].entryID).includes(noFightEl))  // is el.noFight having any of group item
    )
if (place < 0) // not found -> create new group
  {
  let names = new Set().add(el.entryName)                      // create new group
  groups.push( { names, indxs: [indx], weight: el.weight } )  // group constitutive info 
  related.push( names )                                      // keep track of group list
  }
else // find a place in a group
  {
  groups[place].names.add(el.entryName)  // related list is also updated
  groups[place].indxs.push(indx)        // add indx to retreive element in sortedData 
  }
})
return groups.reduce((r,g,i)=> // build result
{
  if (g.indxs.length > 1) {
    let key = `${i}_` + g.indxs.map(x=>sortedData[x].weight).join('_')
    r[key] = []
    g.indxs.forEach(x=> r[key].push( sortedData[x]) )
  }
  return r
}, {} )
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Avinash Thakur