I am currently making a matchmaking system. My target is to limit the array length by 2 data only. In my current work, when I add another data with the same level, it becomes 3 data in 1 array (Please see pic 1). When there’s 3 data in 1 array, send the extra 1 data to another array until it gets a player to match with. The matching works when players have the same level. I have provided my codes below and a screenshot. Any help will be appreciated. Thank you.
const source = [{ id: 1, name: 'player1', level: 1 }, { id: 2, name: 'player2', level: 1 }, { id: 3, name: 'player3', level: 2 }, { id: 4, name: 'player4', level: 2 }, { id: 5, name: 'player5', level: 1 }, { id: 6, name: 'player6', level: 3 }, { id: 7, name: 'player7', level: 3 }, ] const combine = (source) => { return source.reduce((acc, curr) => { if (acc[curr.level]) acc[curr.level].push(curr); else acc[curr.level] = [curr]; return acc; }, {}) } var result = combine(source) var html = "" var keys = Object.keys(result) //if there more then one keys i.e : 2.. for (var i = 0; i < keys.length; i++) { console.log("Keys " + keys[i]) //loop through json array result[keys[i]].forEach(function(val, index) { //check if index value is `0`..change name. var ids = index == 0 ? "id[]" : "idside[]" var name = index == 0 ? "name[]" : "nameside[]" var levels = index == 0 ? "level[]" : "levelside[]" html += `<input type="text" name="${ids}" value="${val.id}"> <input type="text" name="${name}" value="${val.name}"> <input type="text" name="${levels}" value="${val.level}">` }) } document.getElementById("result").innerHTML = html //add html to div console.log(result);
<div id="result"> </div>
Advertisement
Answer
You can accumulate into a 2d array by level and check if the currently accumulated array length is 2.
const source = [{ id: 1, name: 'player1', level: 1 }, { id: 2, name: 'player2', level: 1 }, { id: 3, name: 'player3', level: 2 }, { id: 4, name: 'player4', level: 2 }, { id: 5, name: 'player5', level: 1 }, { id: 6, name: 'player6', level: 3 }, { id: 7, name: 'player7', level: 3 },]; const combine = (source) => { return source.reduce((acc, curr) => { if (acc[curr.level]) { const levelArr = acc[curr.level]; const last = levelArr[levelArr.length - 1]; if (last.length === 2) { levelArr.push([curr]) } else { last.push(curr); } } else { acc[curr.level] = [[curr]]; } return acc; }, {}) }; const result = combine(source) // HTML generation const matchesUl = document.createElement('ul'); Object.entries(result).forEach(([key, level]) => { const levelLi = document.createElement('li'); const title = document.createTextNode(`Level ${key}`); levelLi.appendChild(title); const levelUl = document.createElement('ul'); levelLi.appendChild(levelUl); level.forEach(match => { const [p1, p2] = match; const matchLi = document.createElement('li'); matchLi.textContent = `${p1.name} vs. ${p2?.name ?? '-'}`; levelUl.appendChild(matchLi); }) matchesUl.appendChild(levelLi); }) document.getElementById("result").appendChild(matchesUl); //add html to div // Debug const pre = document.createElement('pre') pre.innerHTML = JSON.stringify(result, null, 2); document.getElementById('debug').appendChild(pre);
#debug { border: 1px solid darkgray; background-color: lightgray; padding: 1rem; margin-top: 1rem; }
<div id="result"> </div> <div id="debug"> <p>Output</p> </div>
If you only want complete combinations you can track individual players in an unmatched
object and only add them to their applicable level array when a match is found. You will still end up with a 2D array for each level, but all sub-arrays will be matched pairs.
const source = [{ id: 1, name: 'player1', level: 1 }, { id: 2, name: 'player2', level: 1 }, { id: 3, name: 'player3', level: 2 }, { id: 4, name: 'player4', level: 2 }, { id: 5, name: 'player5', level: 1 }, { id: 6, name: 'player6', level: 3 }, { id: 7, name: 'player7', level: 3 }, { id: 8, name: 'player8', level: 3 }]; const combine = (source) => { return source.reduce((acc, curr) => { if (acc[curr.level]) { if (acc.unmatched[curr.level]) { acc[curr.level].push([acc.unmatched[curr.level], curr]) acc.unmatched[curr.level] = null; } else { acc.unmatched[curr.level] = curr; } } else { acc[curr.level] = []; acc.unmatched[curr.level] = curr; } return acc; }, { unmatched: {} }) }; const result = combine(source) // HTML Generation const matchesUl = document.createElement('ul'); Object.entries(result).forEach(([key, level]) => { if (key !== 'unmatched') { const levelLi = document.createElement('li'); const title = document.createTextNode(`Level ${key}`); levelLi.appendChild(title); const levelUl = document.createElement('ul'); levelLi.appendChild(levelUl); level.forEach(match => { const [p1, p2] = match; const matchLi = document.createElement('li'); matchLi.textContent = `${p1.name} vs. ${p2?.name ?? '-'}`; levelUl.appendChild(matchLi); }); matchesUl.appendChild(levelLi); } }); const unmatchedLi = document.createElement('li'); const title = document.createTextNode('Unmatched'); unmatchedLi.appendChild(title); const unmatchedUl = document.createElement('ul'); unmatchedLi.appendChild(unmatchedUl); Object.values(result.unmatched).forEach(player => { if (player) { const li = document.createElement('li'); li.textContent = `${player.name} (lvl ${player.level})`; unmatchedUl.appendChild(li); } }); matchesUl.appendChild(unmatchedLi) document.getElementById("result").appendChild(matchesUl); //add html to div // Debug const pre = document.createElement('pre') pre.innerHTML = JSON.stringify(result, null, 2); document.getElementById('debug').appendChild(pre);
#debug { border: 1px solid darkgray; background-color: lightgray; padding: 1rem; margin-top: 1rem; }
<div id="result"> </div> <div id="debug"> <p>Output</p> </div>