Convert played matches to league table - node.js

I play an online FIFA tournament since everyone is safe inside. I want to convert the results to a league table. But I don't know what is the best way to do that.
The data I have is:
matches = [
[{
"homeTeam": "A",
"awayTeam": "B",
"homeGoals": 0,
"awayGoals": 3
},
{
"homeTeam": "D",
"awayTeam": "C",
"homeGoals": 0,
"awayGoals": 3
}],
[{
"homeTeam": "D",
"awayTeam": "B",
"homeGoals": 0,
"awayGoals": 2
},
{
"homeTeam": "A",
"awayTeam": "C",
"homeGoals": 0,
"awayGoals": 1
}]
];
The statistics I want to display are: number of matches played, won, lost & drawn. Goals scored and goals against.
I have a React based web app where I display the results of the matches played.
I host the web app through Firebase so I can use firestore, but is it the best way?

Sure its fine to use Firebase. To do this I would create a class that generates the required data-structure and include the needed functions there to manipulate the data.
League.js
class League {
constructor(matches) {
this.matches = matches;
this.table = {};
}
getStandings() {
this.matches.forEach(match => {
const { homeTeam, awayTeam } = match;
// add teams to the table
if (!this.table[homeTeam]) this.addToTable(homeTeam);
if (!this.table[awayTeam]) this.addToTable(awayTeam);
// increase the played counter
this.increasePlayed([homeTeam, awayTeam]);
// calculate won,lost, drawn
this.setResults(match);
// calculate goalsScored and goalsAgainst
this.setGoals(homeTeam, match.homeGoals, match.awayGoals);
this.setGoals(awayTeam, match.awayGoals, match.homeGoals);
});
// all is done; return the table
return this.table;
}
addToTable(team) {
this.table[team] = {
played: 0,
won: 0,
lost: 0,
drawn: 0,
goalsScored: 0,
goalsAgainst: 0
};
}
increasePlayed(teams) {
teams.forEach(team => this.table[team].played++);
}
setResults(match) {
const {
homeTeam, awayTeam, homeGoals, awayGoals
} = match;
if (homeGoals > awayGoals) {
this.table[homeTeam].won++;
this.table[awayTeam].lost++;
} else if (homeGoals < awayGoals) {
this.table[awayTeam].won++;
this.table[homeTeam].lost++;
} else {
this.table[homeTeam].drawn++;
this.table[awayTeam].drawn++;
}
}
setGoals(team, scored, against) {
this.table[team].goalsScored += scored;
this.table[team].goalsAgainst += against;
}
}
module.exports = League;
Then wherever you need the League, create an instance of it with matches parameter, then simply call getStandings() function to figure and return the table.
app.js
const League = require('./League');
// note that all of the matches objects are flat
const matches = [
{
homeTeam: 'A',
awayTeam: 'B',
homeGoals: 0,
awayGoals: 3
},
{
homeTeam: 'D',
awayTeam: 'C',
homeGoals: 0,
awayGoals: 3
},
{
homeTeam: 'D',
awayTeam: 'B',
homeGoals: 0,
awayGoals: 2
},
{
homeTeam: 'A',
awayTeam: 'C',
homeGoals: 0,
awayGoals: 1
}
];
const league = new League(matches);
const standings = league.getStandings();
console.log(standings);
Now running app.js would output:
{
A: {
played: 2,
won: 0,
lost: 2,
drawn: 0,
goalsScored: 0,
goalsAgainst: 4
},
B: {
played: 2,
won: 2,
lost: 0,
drawn: 0,
goalsScored: 5,
goalsAgainst: 0
},
D: {
played: 2,
won: 0,
lost: 2,
drawn: 0,
goalsScored: 0,
goalsAgainst: 5
},
C: {
played: 2,
won: 2,
lost: 0,
drawn: 0,
goalsScored: 4,
goalsAgainst: 0
}
}
Hope this helps!

Related

How to reduce a 2D array in TypeScript to return a 2D array of unique subarrays?

I'm trying to reduce my 2D array such that there are no duplicate subarrays. I used the .reduce() method to take care of this, however it appears that my conditional statement doesn't work the way one would imagine.
Please refer to the stdout for some additional clarity. Let me know if you know how to make this work or a possible workaround.
const unique = result.reduce<number[][]>((accum, curr) => {
if (!accum.includes(curr)) {
console.log('accum:', accum);
console.log('curr', curr);
return [...accum, curr];
}
else{
return accum;
}
}, []);
console.log('\nresult', result);
console.log('unique', unique);
/*
stdout:
accum: []
curr [ 0, -1, 1 ]
accum: [ [ 0, -1, 1 ] ]
curr [ 0, -1, 1 ]
accum: [ [ 0, -1, 1 ], [ 0, -1, 1 ] ]
curr [ 2, -1, -1 ]
result [ [ 0, -1, 1 ], [ 0, -1, 1 ], [ 2, -1, -1 ] ]
unique [ [ 0, -1, 1 ], [ 0, -1, 1 ], [ 2, -1, -1 ] ]
desired output:
unique [ [ 0, -1, 1 ], [ 2, -1, -1 ] ]
*/
Just use Set. Please keep in mind it wirks only with array of primitive values
const makeUnique = <T extends string | number | symbol>(arr: T[]) => [...new Set(arr)];
const unique2D = [[1, 1, 2], [2, 2, 2, 1], [3, 3, 3]].reduce<number[][]>((acc, elem) =>
[...acc, makeUnique(elem)]
, []
)
Playground
UPDATE
const makeUnique = <T extends string | number | symbol>(arr: T[]) => [...new Set(arr)];
type MainType = number[]
const unique2D = (arr: Array<MainType>) =>
arr.reduce<Array<MainType>>((acc, elem) => [...acc, makeUnique(elem)], [])
const getDublicates = (arr1: Array<MainType>, arr2: MainType) => {
return arr1.reduce((acc, elem, index) =>
elem.length === arr2.length && arr2.every(el => elem.includes(el))
? [...acc, index]
: acc
, [] as MainType)
};
const check = (arr: Array<MainType>) => {
const dublicates = arr.reduce((acc, elem) => makeUnique([...acc, ...getDublicates(arr, [...elem]).slice(1)]), []);
return arr.filter((_, index) => !dublicates.includes(index))
};
const result = check(unique2D([[0, -1, 1], [2, -1, -1], [3, 3], [3], [3], [4], [4]]));
I found a solution that works for the time being. If anyone can solve it with Set or .reduce() please let me know.
const result: number[][] = new Array(); //results obtained later on in code
const Unique: {[key: string]: number} = {}; //store unique results in a hash
const solution: number[][] = new Array();
let target;
//remove possible duplicates:
for (let i = 0; i < result.length; i++) {
let key = JSON.stringify(result[i]);
if (!Unique[key]) {
Unique[key] = 0;
}
}
for (let key in Unique) {
solution.push(JSON.parse(key))
}
return solution;

How to update every document in collection based off another collection with Mongoose

I have a collection with a single document that contains many stats of different people.
It is structured like so:
// Stats list:
[{
id: .... ,
lastUpdated: ... ,
stats: {
Person1: {stat1: 0, stat2: 0, stat3: 0},
Person2: {stat1: 0, stat2: 0, stat3: 0},
...
Person100: {stat1: 0, stat2: 0, stat3: 0}
}
}]
These stats are updated every 24 hours.
Now I have a few hundred listings that contain a specific list of the people in the first collection.
// Listings:
[{
id: ...,
persons: {
Person1: {stat1: 0, stat2: 0, stat3: 0},
Person43: {stat1: 0, stat2: 0, stat3: 0}
}
}]
I want to be able to update all person stats in these listings compared to the persons in the stats section in the first collection I showed.
My logic is something along the lines of this:
await Listing.updateMany({}, { $set: { persons: { "some kind of query" } } });
But I am not sure how to most efficiently go about this.

How do I query a set of objects with an array of values in mongoose?

I have a schema like this
const rankSchema = new Schema(
{
rank: { type: Object, default: {} },
lastUpdated: { type: Date, default: Date.now() },
},
{ minimize: false }
);
And my database has an object 'rank' with many other objects inside of it like this.
rank: {
Person1: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 4 },
Person2: { Stat1: 4, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 2 },
Person3: { Stat1: 1, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 1 },
Person4: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 3 }
}
Now I have an array of strings that contains a few of these people
['Person1', 'Person2']
I want to be able to find all the person objects in that array and return their stats.
So essentially the final output after using the array of strings would be
Person1: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 4 },
Person2: { Stat1: 4, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 2 }
I tried using $in and various different queries but nothing seems to work and I am stumped.
Thanks
You could use a combination of $objectToArray and $arrayToObject to filter your object by dynamic field names but if your parameters are known when you're building your query then it's easier to use regular .find() and apply projection:
db.collection.find({},{ "rank.Person1": 1, "rank.Person2": 1})
let input = ['Person1', 'Person2'];
let entries = input.map(p => ([`rank.${p}`, 1]))
let projection = Object.fromEntries(entries);
console.log(projection);
Mongo Playground

MongoDb multiply aggregate returns null sometimes

I have this aggregate:
const dpi = (imgSize.height * imgSize.width) / (printDpi * printDpi);
let printSizes = await printSizeModel
.aggregate([
{
$project: {
id: 1,
width: 1,
height: 1,
price: 1,
shippingWidth: 1,
shippingHeight: 1,
shippingLength: 1,
shippingWeight: 1,
framePrice: 1,
hasFrame: 1,
total: { $multiply: ['$width', '$height'] },
},
},
{ $match: { total: { $lt: dpi } } },
])
.exec();
Width and height are both numbers and DPi is number as well (DPI is float and width and height are int)
I am using mongoos and Nodejs. This aggregate sometimes returns correct result and sometimes returns null. Based on my understanding this should be correct, but I might be missing something in here
After using Math.ceil to convert the number to Int the issue went away, so I can say that the issue was the float number:
const dpi = Math.ceil((imgSize.height * imgSize.width) / (printDpi * printDpi));
let printSizes = await printSizeModel
.aggregate([
{
$project: {
id: 1,
width: 1,
height: 1,
price: 1,
shippingWidth: 1,
shippingHeight: 1,
shippingLength: 1,
shippingWeight: 1,
framePrice: 1,
hasFrame: 1,
total: { $multiply: ['$width', '$height'] },
},
},
{ $match: { total: { $lt: dpi } } },
])
.exec();

MongoDB MapReduce weird bug

I'm trying this simple MapReduce operation:
function map() {
var gameDay = Math.floor((this.matchCreation - 1427846400000) / 86400000) + 1; // day of april 2015 when the game was played
this.teams.forEach (function (team){
**team.bans.forEach(function (ban){** // says bans is undefined
var value ={
banned : 1,
firstBanned: ( ((ban.pickTurn == 1) || (ban.pickTurn == 2))? 1 : 0 )
}
emit({championId: ban.championId,
day: Number(gameDay)}, value);
emit({championId: ban.championId,
day: "all"}, value);
});
});
}
function reduce(key, values) {
var a = values[0];
for(var i = 1 ; i<values.length ; i++){
var b = values[i]; // will merge 'b' into 'a'
a.banned += (b.banned? b.banned : 0);
a.firstBanned += (b.firstBanned? b.firstBanned : 0);
for (var attrname in b){
if(attrname != "banned" && attrname != "firstBanned")
a[attrname] = b[attrname];
}
}
return a;
}
matchesCollection.mapReduce(map, reduce, {
out: { reduce: "mapReduceResults" }
}, function (err, data){
if(err)
return callback (err);
callback (null, "OK");
});
It used to work before, but just when I tried to deploy the app after testing for a while, it seems to fail in this line: team.bans.forEach(function (ban){, says team.bans is undefined, although every one of the documents has a "teams" array and a "bans" array inside of each object in it, I even double checked it by querying the database and there is no document in which those fields dont exist.
So weird. The reduce function is a bit more complex but it seems to work alright, yet the map one (unlike Reduce, its supposed to be called just once per original document, right?) throws this unexplainable error. Could anyone give me some insight?
Sample input:
{
"_id": {
"$oid": "5531a63f2a3f135c11ed14a8"
},
"matchId": 1778704162,
"region": "NA",
"platformId": "NA1",
"matchMode": "CLASSIC",
"matchType": "MATCHED_GAME",
"matchCreation": 1427864425511,
"matchDuration": 1431,
"queueType": "URF_5x5",
"mapId": 11,
"season": "SEASON2015",
"matchVersion": "5.6.0.194",
"participants": [
{
"teamId": 100,
"spell1Id": 12,
"spell2Id": 4,
"championId": 81,
"highestAchievedSeasonTier": "SILVER",
"timeline": [],
"masteries": [],
"stats": {
"winner": false,
"champLevel": 19,
"item0": 1037,
"item1": 3078,
"item2": 3117,
"item3": 3035,
"item4": 3072,
"item5": 1038,
"item6": 3340,
"kills": 7,
"doubleKills": 1,
"tripleKills": 0,
"quadraKills": 0,
"pentaKills": 0,
"unrealKills": 0,
"largestKillingSpree": 3,
"deaths": 15,
"assists": 9,
"totalDamageDealt": 103191,
"totalDamageDealtToChampions": 22148,
"totalDamageTaken": 32924,
"largestCriticalStrike": 669,
"totalHeal": 2263,
"minionsKilled": 97,
"neutralMinionsKilled": 1,
"neutralMinionsKilledTeamJungle": 1,
"neutralMinionsKilledEnemyJungle": 0,
"goldEarned": 13923,
"goldSpent": 13273,
"combatPlayerScore": 0,
"objectivePlayerScore": 0,
"totalPlayerScore": 0,
"totalScoreRank": 0,
"magicDamageDealtToChampions": 6082,
"physicalDamageDealtToChampions": 15803,
"trueDamageDealtToChampions": 263,
"visionWardsBoughtInGame": 0,
"sightWardsBoughtInGame": 0,
"magicDamageDealt": 45997,
"physicalDamageDealt": 56651,
"trueDamageDealt": 543,
"magicDamageTaken": 25249,
"physicalDamageTaken": 7490,
"trueDamageTaken": 184,
"firstBloodKill": false,
"firstBloodAssist": false,
"firstTowerKill": false,
"firstTowerAssist": false,
"firstInhibitorKill": false,
"firstInhibitorAssist": false,
"inhibitorKills": 0,
"towerKills": 4,
"wardsPlaced": 2,
"wardsKilled": 0,
"largestMultiKill": 2,
"killingSprees": 1,
"totalUnitsHealed": 1,
"totalTimeCrowdControlDealt": 98
},
"participantId": 1,
"runes": []
},
... (9 more like that)
],
"participantIdentities": [],
"teams": [
{
"teamId": 100,
"winner": false,
"firstBlood": true,
"firstTower": false,
"firstInhibitor": true,
"firstBaron": false,
"firstDragon": true,
"towerKills": 6,
"inhibitorKills": 2,
"baronKills": 0,
"dragonKills": 3,
"vilemawKills": 0,
"dominionVictoryScore": 0,
"bans": [
{
"championId": 120,
"pickTurn": 1
},
{
"championId": 37,
"pickTurn": 3
},
{
"championId": 13,
"pickTurn": 5
}
]
},
{
"teamId": 200,
"winner": true,
"firstBlood": false,
"firstTower": true,
"firstInhibitor": false,
"firstBaron": false,
"firstDragon": false,
"towerKills": 11,
"inhibitorKills": 4,
"baronKills": 0,
"dragonKills": 0,
"vilemawKills": 0,
"dominionVictoryScore": 0,
"bans": [
{
"championId": 28,
"pickTurn": 2
},
{
"championId": 38,
"pickTurn": 4
},
{
"championId": 63,
"pickTurn": 6
}
]
}
]
}
Expected output:
{
_id: { championId: Number, day: Number }
value: { banned: Number, firstBanned: Number }
}
After that, its supposed to merge with the results of a previous MapReduce operation, copying all the fields of documents with the same key (in the reduce function), but thats irrelevant now since the error happens before...

Resources