Avoid duplicate pair of json array

Viewed 188

I am making a matchmaking system where entries will be matched if they have the same weight. My target is to avoid the multiple fights of my entries. As we can see on the result of my snippet. We can see that player 1 and player 2 got matched for the first time. After that, player 1 got matched again with player 2 for the second time. My target is to add another if statement to avoid the multiple entry. Any help will be appreciated. Thank you

const source = [
  {
    entryID: 1,
    entryName: "player1",
    weight: 1900,
  },
  {
    entryID: 2,
    entryName: "player1",
    weight: 1900,
  },
  {
    entryID: 3,
    entryName: "player2",
    weight: 1900,
  },
  {
    entryID: 4,
    entryName: "player2",
    weight: 1900,
  },



];

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 ) 
    );
    if (findedAccItem) {
      findedAccItem.push(item);
    } else {
      acc.push([item]);
    }
    return acc;
  }, []);

  const namedDataGroups = dataGroups.reduce((acc, item, index) => {

    const key = [index, ...item.map((item) => item.weight)].join("_");
    acc[key] = item;
    return acc;
  }, {});

  return namedDataGroups;
}


console.log(combine(source));

This is the result of my snippet above:

{
  0_1900_1900: [{
  entryID: 1,
  entryName: "player1",
  weight: 1900
}, {
  entryID: 3,
  entryName: "player2",
  weight: 1900
}],
  1_1900_1900: [{   // player 1 and player 2 got matched again. I need to avoid multiple fight
  entryID: 2,
  entryName: "player1",
  weight: 1900
}, {
  entryID: 4,
  entryName: "player2",
  weight: 1900
}]
}

This is my target result: (

{
    0_1900_1900: [{
    entryID: 1,
    entryName: "player1",
    weight: 1900
  }, {
    entryID: 3,
    entryName: "player2",
    weight: 1900
  }],
    1_1900: [{
    entryID: 2,
    entryName: "player1",
    weight: 1900
  }],
    2_1900: [{
    entryID: 4,
    entryName: "player2",
    weight: 1900
  }]
  }
1 Answers

I'll code it this way instead, given the test set being what it is, I can't guarantee that this code is 100% valid

First answer
(with § used to distinguish the grouped list of entryName for testing purposes)

const source = 
  [ { entryID: 1, entryName: 'player1', weight: 1900 } 
  , { entryID: 2, entryName: 'player1', weight: 1900 } 
  , { entryID: 3, entryName: 'player2', weight: 1900 } 
  , { entryID: 4, entryName: 'player2', weight: 1900 } 
  ]
  
console.log ( combine(source) )

function combine( data = [], different = 0, maxGroupSize = 2
                , sortedStatement = (a, b) => a.weight - b.weight ) 
  {
  const 
    sortedData = [...data].sort(sortedStatement)
  , related = []
  , groups  = []
    ;
  sortedData.forEach((el,indx)=>
    {
    let place = groups.findIndex( g => g.names.length < maxGroupSize 
                                    && !g.names.includes(el.entryName) 
                                    && !related.includes(`${g.names.join('§')}§${el.entryName}`) 
                                    && (el.weight - g.weight) <= different)

    if (place<0)
      {
      groups.push( { names: [ el.entryName], indxs: [indx], weight: el.weight } )  
      related.push( el.entryName )  
      }
    else
      {
      groups[place].names.push(el.entryName)
      groups[place].indxs.push(indx)
      related[place] += `§${el.entryName}`
      }
    })
  return groups.reduce((r,g,i)=>
    {
    let key = `${i}_` + g.indxs.map(x=>sortedData[x].weight).join('_')
    r[key] = []
    g.indxs.forEach(x=> r[key].push( sortedData[x]) )
    return r
    }, {} )
  }
.as-console-wrapper {max-height: 100%!important; top:0; }
.as-console-row::after { display:none !important; }

Second solution using js new Set() (no needs of §)

const source = 
  [ { entryID: 1, entryName: 'player1', weight: 1900 } 
  , { entryID: 2, entryName: 'player1', weight: 1900 } 
  , { entryID: 3, entryName: 'player2', weight: 1900 } 
  , { entryID: 4, entryName: 'player2', weight: 1900 } 
  ]
  
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
    }, {} )
  }
.as-console-wrapper {max-height: 100%!important; top:0; }
.as-console-row::after { display:none !important; }

Related