var players = [
{ id: 1, name : 'player1'},
{ id: 2, name : 'player2'},
{ id: 3, name : 'player3'},
{ id: 4, name : 'player4'},
{ id: 5, name : 'player5'},
{ id: 6, name : 'player6'},
{ id: 7, name : 'player7'},
{ id: 8, name : 'player8'},
{ id: 9, name : 'player9'},
{ id: 10, name : 'player10'},
{ id: 11, name : 'player11'},
{ id: 12, name : 'player12'},
{ id: 13, name : 'player13'},
{ id: 14, name : 'player14'},
{ id: 15, name : 'player15'},
{ id: 16, name : 'player16'}]
I want to toss games with 2 players aginst 2 players.
So one round is 4 games with 2 vs 2.
One player can never be in team with a player it already played with.
I want to build a function that randomize all the games.
so i want something like this but with all the games in the turnament.
Then they play 4 games at the time and then switch players and 4 new games start.
games = [{ team1: [{ id: 1, name : 'player1'},{ id: 2, name : 'player2'}], team2 :[{ id: 3, name : 'player3'},{ id: 4, name : 'player4'}] }]
To get all combinations for a maximal number of possible games rounds (every player plays with each other player exactly once), I used https://math.stackexchange.com/a/3094469 as inspiration.
// From: https://stackoverflow.com/a/12646864/9487478
const shuffleArray = (array) => {
let shuffledArray = [...array];
for (let i = shuffledArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
}
return shuffledArray;
}
// Note: The number of players needs to be even.
let createGames = (playerArr) => {
let players = [...playerArr];
const teamsPerRound = [],
rounds = mod = players.length - 1,
gamesPerRound = 4,
// Helper for checking how often a player is confronted with another player.
confrontations = Array(players.length).fill().map(x => Array(players.length).fill(0));
// Inspired by: https://math.stackexchange.com/a/3094469
// Create as many unique teams as possible, whereas within a round every player occurs exactly once.
for (let i = 0; i < rounds; i++) {
let team = [[
players.length - 1,
(players.length + i) % mod
]];
for (let k = 1; k < (players.length / 2); k++) {
team.push([
(players.length + i + k) % mod,
(players.length + i - k) % mod
]);
}
teamsPerRound.push(team);
console.log(`Teams-Round ${i+1}`, JSON.stringify(team));
}
// Now that we have teams, we can create the games. Let's shuffle the teams per round before to ensure it's more random.
const games = shuffleArray(teamsPerRound).map(teams => {
let roundMatches = [];
teams = shuffleArray(teams);
for (let i = 0; i < teams.length/2; i++) {
let first = teams[i], second = teams[teams.length - 1 - i];
roundMatches.push({
team1: first.map(x => ({...players[x]})),
team2: second.map(x => ({...players[x]}))
})
// Helper for checking how often a player is confronted with another player.
first.forEach(x => second.forEach(y => (confrontations[x][y]++, confrontations[y][x]++)));
}
return roundMatches;
});
confrontations.forEach((x,i) => console.log(`Confrontations (playerIndex: ${i})`, JSON.stringify(x), x.reduce((acc, val) => acc += val)));
return games;
}
var players = [
{ id: 1, name : 'player1'},
{ id: 2, name : 'player2'},
{ id: 3, name : 'player3'},
{ id: 4, name : 'player4'},
{ id: 5, name : 'player5'},
{ id: 6, name : 'player6'},
{ id: 7, name : 'player7'},
{ id: 8, name : 'player8'},
{ id: 9, name : 'player9'},
{ id: 10, name : 'player10'},
{ id: 11, name : 'player11'},
{ id: 12, name : 'player12'},
{ id: 13, name : 'player13'},
{ id: 14, name : 'player14'},
{ id: 15, name : 'player15'},
{ id: 16, name : 'player16'}
];
const games = createGames(players);
console.log("Matches", games);
Related
I made this function to generate custom fixture, but i have an issue if the players in rounds are more than 8. I tried to add 10 playes instead of 8, and the loop return me a wrong result.
let teams = [
{ id: "Team1" },
{ id: "Team2" },
{ id: "Team3" },
{ id: "Team4" },
{ id: "Team5" },
{ id: "Team6" },
{ id: "Team7" },
{ id: "Team8" },
{id: "Team9" },
{id: "Team10" }
]
// Total rounds
let totRounds = 3
// Array with rounds
let rounds = []
for (let i = 0; i < totRounds; i++) {
// This loop add an array on enemyes teams enemies["team2,..."]
teams.forEach(team => team.enemies = teams.filter(enemy => enemy !== team));
// Empty Array for first round
const matches = [];
// Empty Array for second round ( return )
const matches_return = [];
while (teams.some(team => team.enemies.length)) {
const playing = [];
const playing_return = []
for (const team of teams) {
//debugger
if (playing.includes(team)) continue;
const enemy = team.enemies.find(enemy => !playing.includes(enemy));
if (!enemy) continue;
team.enemies.splice(team.enemies.indexOf(enemy), 1);
enemy.enemies.splice(enemy.enemies.indexOf(team), 1);
playing.push(team, enemy);
playing_return.push(enemy, team);
}
if (playing.length) matches.push(playing.map(t => t.id))
if (playing_return.length) matches_return.push(playing_return.map(t => t.id))
}
// Merge 2 arrays
const totalMatches = matches.concat(matches_return)
rounds.push(totalMatches);
}
console.log(rounds);
This work with 8 players, but with 10 or more not. I tried to made some changes but they didn't work.
I am trying to make function to get top3 objects from an array based by props. My site can't load up so i think this function runs endlessly but i cant figure out why.
renderItems = (items) => {
let top3 = []
let tempArr = items
let allNumbers = []
while (top3.length < 3){
allNumbers = []
for(let i = 0; i < tempArr.length; i++){
allNumbers = [...allNumbers, tempArr[i].hearts]
}
const result = tempArr.filter(i => i.hearts === Math.max(...allNumbers))
top3 = [...top3, ...result]
let countOfdeleted = 0
for(let i = 0; i < result.length; i++){
tempArr.splice(result[i].id-countOfdeleted, 1)
countOfdeleted++
}
for(let i = 0; i < tempArr.length; i++){
tempArr[i].id = i
}
}
console.log(top3);
}
This answer is based on the assumption that 'items' is an array of objects and that each object in 'items' will have at-least 2 props namely 'id' and 'hearts'.
Further, there is no clarity on the significance of 'countOfdeleted' and 'tempArr'. Hence, it is assumed
that one needs to know how many elements of the 'items' array were not included (in the top3) as 'countOfdeleted'
that the remaining objects need to have their 'id' updated (based on index)
With the aforementioned assumptions, the below should implement the required logic:
const items = [
{ id: 0, hearts: 5 }, { id: 1, hearts: 4 }, { id: 2, hearts: 5 },
{ id: 3, hearts: 3 }, { id: 4, hearts: 5 }, { id: 5, hearts: 2 },
{ id: 6, hearts: 2 }, { id: 7, hearts: 1 }, { id: 8, hearts: 4 }
];
const getTop3 = (arr = items) => {
const maxVal = arr.reduce((fin, itm) => itm.hearts > fin ? itm.hearts : fin, 0);
const topAll = arr.filter(obj => obj.hearts === maxVal);
const sansTop3 = arr
.filter(obj => obj.hearts !== maxVal)
.map((obj, idx) => ({...obj, id: idx}));
console.log('countOfDeleted: ', arr.length - (topAll.length > 3 ? topAll.length : 3));
console.log('rest with updated-id (tempArr): ', sansTop3);
return topAll.length > 3 ? topAll.slice(0, 3) : [...topAll];
};
console.log('\ntop3:\n^^^^\n', getTop3());
Approach / Explanation
Find the 'maximum-value' (maxVal) based on the prop ('hearts')
Find all objects which have the props matching maxVal (stored in array 'topAll')
[Optional: Gather remaining 'items' elements and update their 'id' in 'sansTop3' array, to match the 'tempArr' in the question]
[Optional: Determine the number of 'items' deleted, to match countOfdeleted in the question]
If more than 3 elements have props ('heart') matching 'maxVal', return only 3; otherwise, return the all top-value element/s
I want to store the "node indentation string" for each object, something like this:
foo
┣bar
┃┗baz
┃ ┗qux
┃ ┣quux
┃ ┗corge
┣fizz
┗buzz
Given data for each object:
objects = [
{'id':1,'parent_id':null, 'name':'foo'}
{'id':2,'parent_id':1, 'name':'bar'}
];
Note that I don't want to print anything, I just want to work out the indent as an array of characters for each object:
{'id':6,'parent_id':4, 'name':'corge', 'indent':['┃',' ',' ','┗']}
So far I can only indent them with spaces but no 'pipes' and I am stumped at coming up with a solution. Any help?
I am using JS with Angular if it helps.
EDIT: As requested the code I have so far. I didn't post this at first because I felt that it's a wrong foundation/approach to build on. How it works is pretty trivial: for each object, count it's ancestors and add " "'s accordingly.
// go through all our objects and set their indent strings
setIndents = function()
{
for (var x in objects) {
var o = objects[x];
o.nodes = [];
// push space character for amount of ancestors
numParents = countParents(o, 0);
for (var i = 0; i < numParents; i++)
o.nodes.push(" ");
}
};
// recursively counts how many ancestors until we hit the root
countParents = function(current, count)
{
if (current.parent_id !== null) {
for (var x in objects) {
if (objects[x].id == current.parent_id) {
current = objects[x]; //set as new current
count++;
break;
}
}
return countParents(current, count);
} else {
return count;
}
};
As #JBCP pointed out (see comments) there is a serious flaw in my original code that would break the whole thing if the initial order was anything but perfect.
So here's an updated version, the order of elements can now be random (it still plays a role in such that it indirectly defines the children order, but the tree-structure will be correct).
I also split the functions so that they can be better configured. For example treeIndent now expects a node branch produced by treeify. (Note: the shuffle function is just there to test the order independence)
'use strict';
/**
* #see https://bost.ocks.org/mike/shuffle/
*
* #param array
* #returns {*}
*/
function shuffle(array) {
var m = array.length, t, i;
// While there remain elements to shuffle…
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
function treeify(flat) {
var map = { __root__: { children: [] }};
flat.forEach(function (node) {
var
parentId = node.parent_id || '__root__',
id = node.id;
// init parent
if (!map.hasOwnProperty(parentId)) {
map[parentId] = { element: null, children: [] };
}
// init self
if (!map.hasOwnProperty(id)) {
map[id] = { element: null, children: [] };
}
map[id].element = node;
map[parentId].children.push(map[id]);
});
return map.__root__.children;
}
function treeIndent(branch, cfg, decorator, indent)
{
indent = indent || [];
branch.forEach(function (node, i) {
decorator(node.element, indent.concat(
i === branch.length - 1 ? cfg.isLastChild : cfg.hasNextSibling
));
treeIndent(node.children, cfg, decorator, indent.concat(
i === branch.length - 1 ? cfg.ancestorIsLastChild : cfg.ancestorHasNextSibling
));
});
}
var input = [
{ id: 1, parent_id: null, name: 'root' },
{ id: 2, parent_id: 1, name: 'bar' },
{ id: 5, parent_id: 2, name: 'baz' },
{ id: 6, parent_id: 5, name: 'qux' },
{ id: 7, parent_id: 6, name: 'quux' },
{ id: 8, parent_id: 6, name: 'corge' },
{ id: 9, parent_id: 2, name: 'but' },
{ id: 3, parent_id: 1, name: 'fizz' },
{ id: 4, parent_id: 1, name: 'buzz' }
];
var log = document.getElementById('log');
treeIndent(treeify(shuffle(input)), {
hasNextSibling: '├',
isLastChild: '└',
ancestorHasNextSibling: '│',
ancestorIsLastChild: ' '
}, function (element, indent) {
log.innerHTML += indent.join(' ') + ' ' + element.name + "\n";
});
<pre id="log"></pre>
Old answer (broken!):
try the following:
function makeTree(flat) {
var map = { __root__: { children: [] }};
flat.forEach(function (node) {
var
parentId = node.parent_id || '__root__',
id = node.id;
// init parent
if (!map.hasOwnProperty(parentId)) {
map[parentId] = { children: [] };
}
// init self
if (!map.hasOwnProperty(id)) {
map[id] = { children: [] };
}
map[id].element = node;
map[parentId].children.push(map[id]);
});
return map.__root__.children;
}
function injectTreeIndent(input) {
var
levelMap = [],
indicators = {
hasNextSibling: '┣',
isLastChild: '┗',
ancestorHasNextSibling: '┃',
ancestorIsLastChild: ' '
}
;
// apply `indent`
(function traverse(branch, depth) {
branch.forEach(function (node, idx) {
node.element.indent = levelMap.map(function (ancestor) {
return ancestor === indicators.hasNextSibling ? indicators.ancestorHasNextSibling : indicators.ancestorIsLastChild;
});
// if (depth > 0) { // uncomment this, if root elements should have no indentation
node.element.indent.push(
levelMap[depth] = branch.length - 1 > idx ? indicators.hasNextSibling : indicators.isLastChild
);
// }
traverse(node.children, depth + 1);
levelMap.pop();
});
}(makeTree(input), 0));
}
var input = [
{ id: 1, parent_id: null, name: 'foo' },
{ id: 2, parent_id: 1, name: 'bar' },
{ id: 5, parent_id: 2, name: 'baz' },
{ id: 6, parent_id: 5, name: 'qux' },
{ id: 7, parent_id: 6, name: 'quux' },
{ id: 8, parent_id: 6, name: 'corge' },
{ id: 3, parent_id: 1, name: 'fizz' },
{ id: 4, parent_id: 1, name: 'buzz' }
];
injectTreeIndent(input);
makeTree is used to optain a nested structure derived from the given flat data.
injectTreeIndent then traverses that nested structure to inject the required indent informatoin.
demo: http://jsfiddle.net/6R7wf/1/
demo with root elements having no indenation: http://jsfiddle.net/zMY7v/
After for (var i = 0; i < numParents; i++) o.nodes.push(" ");, try
if (o.nodes.length === 1)
o.nodes[0] = "┣";
else if (o.nodes.length > 1) {
o.nodes[0] = "┃";
o.nodes[o.nodes.length - 1] = "┗";
}
I have been trying for hours to do this using json.js but is just too much for something that seems simple. I have this example data:
var hotels = [
{ id: 101, Name: "Hotel 101", WebFacilities: [8, 9, 10] },
{ id: 102, Name: "Hotel 101", WebFacilities: [8] },
{ id: 103, Name: "Hotel 101", WebFacilities: [8, 10] }
];
var facilities = [
{ id: 8, Name: "Facility 8" },
{ id: 9, Name: "Facility 9" },
{ id: 10, Name: "Facility 10" }
];
I want to get this:
var selectedFacilities = [
{ id: 8, Name: "Facility 8", Count: 3 },
{ id: 9, Name: "Facility 9", Count: 1 },
{ id: 10, Name: "Facility 10", Count: 2 }
];
How do I do this?
So it appears you're trying to count how many of each facility there is.
Here's one way to write the query using C#:
var hotelFacilities =
from hotel in hotels
from id in hotel.WebFacilities
group id by id;
var query =
from facility in facilities
join g in hotelFacilities on facility.id equals g.Key
select new
{
id = facility.id,
Name = facility.Name,
Count = g.Count(),
};
Now if you can picture this using the method syntax, it is almost a 1:1 transformation to the linq.js version.
Note the way the compiler translates the above will usually include the GroupBy() call within the previous SelectMany() call. However written this way will make writing the linq.js equivalent query easier and less awkward.
var hotelFacilities = hotels
.SelectMany(hotel => hotel.WebFacilities)
.GroupBy(id => id);
var query = facilities
.Join(
hotelFacilities,
facility => facility.id,
g => g.Key,
(facility, g) => new
{
id = facility.id,
Name = facility.Name,
Count = g.Count(),
}
);
And the equivalent linq.js query.
var hotelFacilities = Enumerable.From(hotels)
.SelectMany("hotel => hotel.WebFacilities")
.GroupBy("id => id")
.ToArray();
var query = Enumerable.From(facilities)
.Join(
hotelFacilities,
"facility => facility.id",
"g => g.Key()",
"(facility, g) => { id: facility.id, Name: facility.Name, Count: g.Count() }"
).ToArray();
Use this:
var selectedFacilities = facilities;
for(var i = 0; i < facilities.length; i++) {
for(var j = 0; j < hotels.length; j++) {
if(hotels[j]["id"] == facilities[i]["id"]) {
// Add data
selectedFacilities[i]["Count"] = hotels[i]["WebFacilities"].length;
} else {
selectedFacilities[i]["Count"] = 0;
}
}
}
a collection is returning 11 items as follows;
( 1, "Ball", "Result1")
( 2, "Ball", " Result2")
( 3, "Ball", " Result3")
( 4, "Ball", " Result4")
( 5, "Ball", " Result5")
( 6, "Ball", " Result6")
( 7, "Ball", " Result7")
( 8, "Ball", " Result8")
( 9, "Pool", " Pool 1")
( 10, "Pool", " Pool 2")
( 11, "Pool", " Pool 3")
I want to store them, group them as four items.. so that my array looks like this
var data = [];
data.push({
myclass: "First4",
schedule: [ {
id : '1',
gameType: 'Ball',
result: 'Result11'
}, {
id: '2',
gameType: 'Ball',
result: 'Result2'
},........... ]
});
//second group
data.push({
divClass : "second4",
items : [ {
id : '5'
gameType: 'Ball',
result: 'Result5'
}, {
id : ''
gameType: 'Ball',
result: 'Result6
} ]
});
how can i write a for loop so that i can achieve the same result dynamically instead of writing the push manually
for(var i = 0; i < collLength; i++){
// do push 1 with first four //data.push(class, sheculde first 4 items, result)
// do second push with second four
// do third push with the remaining
}
var data = [];
for(var i = 0; i < collLength; i+=4) {
data.push({
divClass: "group" + (i / 4),
items: collection.slice(i, i + 4).map(function(item) {
return {id:item[0], gameType:item[1], result:item[2]};
})
});
}
var indata = [...]; // this is filled with your list of objects.
var outdata = [ ];
var n = indata.length;
for (var i = 0; i < n; i += 4)
{
outdata.push({
class: 'group' + outdata.length,
items: indata.slice(i, i + 4)
});
}
var indata = [...]; // this is filled with your list of objects.
var outdata = [ ];
function mapper(element, i)
{
var j = Math.floor(i / 4);
if (outdata[j] == undefined)
outdata[j] = { class: 'group' + j, items: [ ] };
outdata[j].items.push(element);
}
indata.map(mapper);
Here's a way using .reduce().
var data = collection.reduce(function(arr, obj, i) {
return i % 4 ? arr :
arr.concat({
divClass: "group" + (i / 4),
items: collection.slice(i, i + 4).map(function(item) {
return {id:item[0], gameType:item[1], result:item[2]};
})
});
}, []);