462 lines
24 KiB
Vue
462 lines
24 KiB
Vue
<template>
|
|
<div class="leaderboard-view centerDiv">
|
|
<div class="row">
|
|
<div class="col-sm-12">
|
|
<h1 id="season-heading">Season {{ seasonNumber }} Leaders</h1>
|
|
<h2 id="week-num">Week {{ weekNumber }}</h2>
|
|
<h2 id="stat-heading">{{ statType }} Leaderboards - Min {{ statMinimum }}</h2>
|
|
<!-- <label for="seasonNumber">Select Season</label> -->
|
|
<select name="seasonNumber" id="seasonNumber" v-model="seasonNumber">
|
|
<option v-for="season in seasonNumbers" :key="season" :value="season">Season {{ season }}</option>
|
|
</select>
|
|
<!-- TODO ADD SORT FOR ASC/DESC -->
|
|
<select name="statType" id="statType" v-model="statType">
|
|
<option key="Batting" value="Batting">Batting</option>
|
|
<option key="Pitching" value="Pitching">Pitching</option>
|
|
<option key="Fielding" value="Fielding">Fielding</option>
|
|
</select>
|
|
<select v-if="statType === 'Pitching'" name="pitchingType" id="pitchingType" v-model="pitchingType">
|
|
<option key="All" value="All">All</option>
|
|
<option key="Starters" value="Starters">Starters Only</option>
|
|
<option key="Relievers" value="Relievers">Relievers Only</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row" id="stat-listing">
|
|
<div v-for="table in leaderboardTableData" :key="table.statPropertyName" class="col-sm-auto"
|
|
style="text-align: center">
|
|
<h3><small>{{ table.title }}</small></h3>
|
|
<div class="table-responsive-xl">
|
|
<table style="text-align: center" class="table table-sm table-striped">
|
|
<thead class="thead-dark">
|
|
<tr>
|
|
<th style="min-width: 150px">Player</th>
|
|
<th style="min-width: 75px">Team</th>
|
|
<th style="min-width: 75px">{{ table.columnName }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="stat in getTop10StatByCategory(table.statPropertyName)" :key="stat.player.name">
|
|
<td>
|
|
<RouterLink
|
|
:to="{ name: 'player', params: { seasonNumber: seasonNumber, playerName: stat.player.name } }">
|
|
{{ stat.player.name }}
|
|
</RouterLink>
|
|
</td>
|
|
<td>
|
|
<RouterLink v-if="stat.team.abbrev"
|
|
:to="{ name: 'team', params: { seasonNumber: seasonNumber, teamAbbreviation: stat.team.abbrev } }">
|
|
{{ stat.team.abbrev }}
|
|
</RouterLink>
|
|
</td>
|
|
<td>{{ formatNumericalStat(stat, table.statPropertyName, table.precision || 0) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import type { Team } from '@/services/apiResponseTypes'
|
|
import { fetchBattingStatsBySeason, type BattingStat } from '@/services/battingStatsService'
|
|
import { type LeagueInfo, fetchCurrentLeagueInfo } from '@/services/currentService'
|
|
import { fetchFieldingStatsBySeason, type FieldingStat } from '@/services/fieldingStatsService'
|
|
import { fetchPitchingStatsBySeason, type PitchingStat } from '@/services/pitchingStatsService'
|
|
import type { Player } from '@/services/playersService'
|
|
import { fetchTeamsBySeason } from '@/services/teamsService'
|
|
import { CURRENT_SEASON, GAMES_PER_WEEK, MODERN_STAT_ERA_START, WEEKS_PER_SEASON } from '@/services/utilities'
|
|
|
|
interface LeaderboardTableData<T> {
|
|
title: string
|
|
columnName: string
|
|
statPropertyName: keyof T
|
|
precision?: number
|
|
}
|
|
|
|
interface FlatFieldingStat {
|
|
player: Player
|
|
team: Team
|
|
xCheckCount: number
|
|
hit: number
|
|
error: number
|
|
stolenBaseCheckCount: number
|
|
stolenBaseCount: number
|
|
caughtStealingCount: number
|
|
caughtStealingPercent: number
|
|
passedBallCount: number
|
|
xCheckCountPitcher: number
|
|
weightedFieldingPercentPitcher: number
|
|
xCheckCountCatcher: number
|
|
weightedFieldingPercentCatcher: number
|
|
xCheckCountFirstBase: number
|
|
weightedFieldingPercentFirstBase: number
|
|
xCheckCountSecondBase: number
|
|
weightedFieldingPercentSecondBase: number
|
|
xCheckCountThirdBase: number
|
|
weightedFieldingPercentThirdBase: number
|
|
xCheckCountShortstop: number
|
|
weightedFieldingPercentShortstop: number
|
|
xCheckCountLeftField: number
|
|
weightedFieldingPercentLeftField: number
|
|
xCheckCountCenterField: number
|
|
weightedFieldingPercentCenterField: number
|
|
xCheckCountRightField: number
|
|
weightedFieldingPercentRightField: number
|
|
}
|
|
|
|
export default {
|
|
name: 'LeaderboardView',
|
|
data() {
|
|
return {
|
|
allPlayersBattingStats: [] as BattingStat[],
|
|
allPlayersFieldingStats: [] as FlatFieldingStat[],
|
|
allPlayersPitchingStats: [] as PitchingStat[],
|
|
teamsById: {} as Map<number, Team>,
|
|
currentSeasonNumber: CURRENT_SEASON,
|
|
seasonNumber: CURRENT_SEASON,
|
|
statType: 'Batting' as 'Batting' | 'Pitching' | 'Fielding',
|
|
pitchingType: 'All' as 'All' | 'Starters' | 'Relievers',
|
|
weekNumber: undefined! as number
|
|
}
|
|
},
|
|
computed: {
|
|
seasonNumbers(): number[] {
|
|
if (!this.currentSeasonNumber) return []
|
|
return Array.from({ length: this.currentSeasonNumber - MODERN_STAT_ERA_START + 1 }, (_, i) => i + MODERN_STAT_ERA_START)
|
|
},
|
|
weekNumberForCalcs(): number {
|
|
if (this.seasonNumber < this.currentSeasonNumber) return WEEKS_PER_SEASON
|
|
// since stats are for regular season, do not use post season weeks to up PA/IP mins
|
|
return Math.max(0, Math.min(this.weekNumber, WEEKS_PER_SEASON))
|
|
},
|
|
statMinimum(): string {
|
|
if (this.statType == 'Batting') return `PA: ${this.plateAppearanceMinimum}`
|
|
if (this.statType == 'Pitching') return `IP: ${this.inningsPitchedMinimum}`
|
|
return `${Math.floor(this.weekNumberForCalcs * 2)}/${Math.floor(this.weekNumberForCalcs)}/${Math.floor(this.weekNumberForCalcs * 0.5)}/${Math.floor(this.weekNumberForCalcs * 0.25)}`
|
|
},
|
|
plateAppearanceMinimum(): number {
|
|
const MIN_PLATE_APPEARANCES_PER_GAME = 3
|
|
return this.weekNumberForCalcs * GAMES_PER_WEEK * MIN_PLATE_APPEARANCES_PER_GAME
|
|
},
|
|
inningsPitchedMinimum(): number {
|
|
if (this.pitchingType === 'Relievers') return this.weekNumberForCalcs
|
|
|
|
const MIN_IP_PER_GAME = 1
|
|
return this.weekNumberForCalcs * GAMES_PER_WEEK * MIN_IP_PER_GAME
|
|
},
|
|
qualifyingBattingStats(): BattingStat[] {
|
|
return this.allPlayersBattingStats.filter(bs => bs.pa >= this.plateAppearanceMinimum)
|
|
},
|
|
qualifyingPitchingStats(): PitchingStat[] {
|
|
return this.allPlayersPitchingStats.filter(ps => ps.ip >= this.inningsPitchedMinimum)
|
|
},
|
|
leaderboardTableData(): LeaderboardTableData<BattingStat>[] | LeaderboardTableData<PitchingStat>[] | LeaderboardTableData<FlatFieldingStat>[] {
|
|
if (this.statType == 'Batting') {
|
|
return this.battingLeaderboardTableData
|
|
}
|
|
if (this.statType == 'Pitching') {
|
|
return this.pitchingLeaderboardTableData
|
|
}
|
|
|
|
return this.fieldingLeaderboardTableData
|
|
},
|
|
battingLeaderboardTableData(): LeaderboardTableData<BattingStat>[] {
|
|
return [
|
|
{ title: 'Home Runs', columnName: 'HR', statPropertyName: 'hr' },
|
|
{ title: 'Batting Average', columnName: 'AVG', statPropertyName: 'avg', precision: 3 },
|
|
{ title: 'On Base Percentage', columnName: 'OBP', statPropertyName: 'obp', precision: 3 },
|
|
{ title: 'Slugging Percentage', columnName: 'SLG', statPropertyName: 'slg', precision: 3 },
|
|
{ title: 'On Base Plus Slugging', columnName: 'OPS', statPropertyName: 'ops', precision: 3 },
|
|
{ title: 'Weighted On Base Avg', columnName: 'wOBA', statPropertyName: 'woba', precision: 3 },
|
|
{ title: 'Runs Batted In', columnName: 'RBI', statPropertyName: 'rbi' },
|
|
{ title: 'Doubles', columnName: '2B', statPropertyName: 'double' },
|
|
{ title: 'Triples', columnName: '3B', statPropertyName: 'triple' },
|
|
{ title: 'Hits', columnName: 'H', statPropertyName: 'hit' },
|
|
{ title: 'Plate Appearances', columnName: 'PA', statPropertyName: 'pa' },
|
|
{ title: 'Ground Into Double Play', columnName: 'GIDP', statPropertyName: 'gidp' },
|
|
{ title: 'Walks', columnName: 'BB', statPropertyName: 'bb' },
|
|
{ title: 'Strikeouts', columnName: 'K', statPropertyName: 'so' },
|
|
{ title: 'Hit By Pitch', columnName: 'HBP', statPropertyName: 'hbp' },
|
|
{ title: 'Intentional Walks', columnName: 'IBB', statPropertyName: 'ibb' },
|
|
{ title: 'Stolen Bases', columnName: 'SB', statPropertyName: 'sb' },
|
|
{ title: 'Caught Stealing', columnName: 'CS', statPropertyName: 'cs' },
|
|
{ title: 'Runs Scored', columnName: 'R', statPropertyName: 'run' },
|
|
{ title: 'At Bats', columnName: 'AB', statPropertyName: 'ab' },
|
|
// { title: 'No-Doubt Home Runs', columnName: 'NDHR', battingStatProperty: 'dong' },
|
|
{ title: 'Ballpark Home Runs', columnName: 'BPHR', statPropertyName: 'bphr' },
|
|
{ title: 'Ballpark Flyouts', columnName: 'BPFO', statPropertyName: 'bpfo' },
|
|
{ title: 'Ballpark Singles', columnName: 'BP1B', statPropertyName: 'bp1b' },
|
|
{ title: 'Ballpark Lineouts', columnName: 'BPLO', statPropertyName: 'bplo' },
|
|
]
|
|
},
|
|
pitchingLeaderboardTableData(): LeaderboardTableData<PitchingStat>[] {
|
|
return [
|
|
// { title: 'Starter Wins', columnName: 'W', statPropertyName: 'win' },
|
|
{ title: 'Wins', columnName: 'W', statPropertyName: 'win' },
|
|
{ title: 'Losses', columnName: 'L', statPropertyName: 'loss' },
|
|
{ title: 'Earned Run Average', columnName: 'ERA', statPropertyName: 'era', precision: 2 },
|
|
{ title: 'Walks + Hits / IP', columnName: 'WHIP', statPropertyName: 'whip', precision: 3 },
|
|
|
|
{ title: 'Innings Pitched', columnName: 'IP', statPropertyName: 'ip', precision: 1 },
|
|
{ title: 'Runs Allowed', columnName: 'R', statPropertyName: 'run' },
|
|
{ title: 'Earned Runs Allowed', columnName: 'ER', statPropertyName: 'e_run' },
|
|
{ title: 'Holds', columnName: 'HD', statPropertyName: 'hold' },
|
|
|
|
{ title: 'Saves', columnName: 'SV', statPropertyName: 'save' },
|
|
{ title: 'Blown Saves', columnName: 'BSV', statPropertyName: 'bsave' },
|
|
{ title: 'Strikeouts', columnName: 'K', statPropertyName: 'so' },
|
|
{ title: 'Strikeouts Per Nine', columnName: 'K/9', statPropertyName: 'kPer9', precision: 2 },
|
|
|
|
{ title: 'Walks Allowed', columnName: 'BB', statPropertyName: 'bb' },
|
|
{ title: 'Walks Per Nine', columnName: 'BB/9', statPropertyName: 'bbPer9', precision: 2 },
|
|
{ title: 'Strikeouts Per Walk', columnName: 'K/BB', statPropertyName: 'kPerBB', precision: 2 },
|
|
{ title: 'Hit By Pitches Allowed', columnName: 'HBP', statPropertyName: 'hbp' },
|
|
|
|
{ title: 'Hits Allowed', columnName: 'H', statPropertyName: 'hits' },
|
|
{ title: 'Home Runs Allowed', columnName: 'HR', statPropertyName: 'hr' },
|
|
{ title: 'Balks', columnName: 'BK', statPropertyName: 'balk' },
|
|
{ title: 'Wild Pitches', columnName: 'WP', statPropertyName: 'wp' },
|
|
|
|
{ title: 'Inherited Runners', columnName: 'IR', statPropertyName: 'ir' },
|
|
{ title: 'Inherited Runners Scored', columnName: 'IRS', statPropertyName: 'ir_sc' }
|
|
]
|
|
},
|
|
fieldingLeaderboardTableData(): LeaderboardTableData<FlatFieldingStat>[] {
|
|
return [
|
|
{ title: 'Fielding Chances', columnName: 'X-Ch', statPropertyName: 'xCheckCount' },
|
|
{ title: 'Weighted Fielding % (RF)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentRightField', precision: 3 },
|
|
{ title: 'Weighted Fielding % (CF)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentCenterField', precision: 3 },
|
|
{ title: 'Weighted Fielding % (LF)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentLeftField', precision: 3 },
|
|
|
|
{ title: 'Weighted Fielding % (SS)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentShortstop', precision: 3 },
|
|
{ title: 'Weighted Fielding % (3B)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentThirdBase', precision: 3 },
|
|
{ title: 'Weighted Fielding % (2B)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentSecondBase', precision: 3 },
|
|
{ title: 'Weighted Fielding % (1B)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentFirstBase', precision: 3 },
|
|
|
|
{ title: 'Weighted Fielding % (C)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentCatcher', precision: 3 },
|
|
{ title: 'Weighted Fielding % (P)', columnName: 'wF%', statPropertyName: 'weightedFieldingPercentPitcher', precision: 3 },
|
|
{ title: 'Errors', columnName: 'E', statPropertyName: 'error' },
|
|
{ title: 'Hits Allowed', columnName: 'X-Hit', statPropertyName: 'hit' },
|
|
|
|
{ title: 'Passed Balls', columnName: 'PB', statPropertyName: 'passedBallCount' },
|
|
{ title: 'Steal Attempts Against', columnName: 'SBa', statPropertyName: 'stolenBaseCheckCount' },
|
|
{ title: 'Runners Thrown Out (C)', columnName: 'CSc', statPropertyName: 'caughtStealingCount' },
|
|
{ title: 'Caught Stealing % (C)', columnName: 'CS%', statPropertyName: 'caughtStealingPercent', precision: 3 },
|
|
]
|
|
}
|
|
},
|
|
created() {
|
|
this.fetchData()
|
|
},
|
|
watch: {
|
|
seasonNumber(newValue, oldValue) {
|
|
if (newValue !== oldValue)
|
|
this.fetchData()
|
|
},
|
|
statType(newValue, oldValue) {
|
|
if (newValue !== oldValue)
|
|
this.fetchData()
|
|
}
|
|
},
|
|
methods: {
|
|
getTop10StatByCategory(stat: keyof BattingStat | keyof PitchingStat | keyof FlatFieldingStat): (BattingStat | PitchingStat | FlatFieldingStat)[] {
|
|
if (this.statType == 'Batting') {
|
|
return this.getTop10BattingStatByCategory(stat as keyof BattingStat)
|
|
}
|
|
if (this.statType == 'Pitching') {
|
|
return this.getTop10PitchingStatByCategory(stat as keyof PitchingStat)
|
|
}
|
|
|
|
return this.getTop10FieldingStatByCategory(stat as keyof FlatFieldingStat)
|
|
},
|
|
getTop10BattingStatByCategory(stat: keyof BattingStat): BattingStat[] {
|
|
const paExemptStat: (keyof BattingStat)[] = ['hr', 'rbi', 'double', 'triple', 'hit', 'gidp', 'bb', 'so', 'hbp', 'ibb', 'sb', 'cs', 'run', 'bphr', 'bpfo', 'bp1b', 'bplo', 'ab', 'pa']
|
|
|
|
// concat an empty array so that the existing array is not sorted as a side effect
|
|
const statBase: BattingStat[] = paExemptStat.includes(stat) ? this.allPlayersBattingStats.concat([]) : this.qualifyingBattingStats.concat([])
|
|
|
|
// concat an empty array so that the existing array is not sorted as a side effect
|
|
return statBase.sort((a, b) => (b[stat] as number) - (a[stat] as number)).slice(0, 10)
|
|
},
|
|
getTop10PitchingStatByCategory(stat: keyof PitchingStat): PitchingStat[] {
|
|
// qualifying IP exempt stats
|
|
const ipExemptStats: (keyof PitchingStat)[] = ['win', 'loss', 'save', 'bsave', 'hold', 'so', 'bb', 'hbp', 'hits', 'hr', 'run', 'e_run', 'balk', 'wp', 'ir', 'ir_sc']
|
|
|
|
// concat an empty array so that the existing array is not sorted as a side effect
|
|
const statBase: PitchingStat[] = ipExemptStats.includes(stat) ? this.allPlayersPitchingStats.concat([]) : this.qualifyingPitchingStats.concat([])
|
|
// filter based on pitchingType selection
|
|
const filteredStatBase: PitchingStat[] = statBase.filter(s =>
|
|
this.pitchingType === 'All'
|
|
? true
|
|
: this.pitchingType === 'Starters'
|
|
? s.gs > 0
|
|
: s.gs === 0
|
|
)
|
|
|
|
const reverseSortStats = ['era', 'whip', 'bbPer9']
|
|
const sortMultiplier = reverseSortStats.includes(stat) ? -1 : 1
|
|
|
|
return filteredStatBase.sort((a, b) => sortMultiplier * ((b[stat] as number) - (a[stat] as number))).slice(0, 10)
|
|
},
|
|
getTop10FieldingStatByCategory(stat: keyof FlatFieldingStat): FlatFieldingStat[] {
|
|
// High: 2B/SS - 2 XCh/wk
|
|
// Med: 1B/3B/CF - 1 XCh/wk
|
|
// Low: LF/RF/C - 0.5 XCh/wk
|
|
// X-Low: P - 0.25 XCh/wk
|
|
const highXCheckGroup: (keyof FlatFieldingStat)[] = ['weightedFieldingPercentSecondBase', 'weightedFieldingPercentShortstop']
|
|
const mediumXCheckGroup: (keyof FlatFieldingStat)[] = ['weightedFieldingPercentFirstBase', 'weightedFieldingPercentThirdBase', 'weightedFieldingPercentCenterField']
|
|
const lowXCheckGroup: (keyof FlatFieldingStat)[] = ['weightedFieldingPercentLeftField', 'weightedFieldingPercentRightField', 'weightedFieldingPercentCatcher', 'weightedFieldingPercentCatcher']
|
|
|
|
if ([...highXCheckGroup, ...mediumXCheckGroup, ...lowXCheckGroup, 'weightedFieldingPercentPitcher'].includes(stat)) {
|
|
const xCheckCountPerWeek = highXCheckGroup.includes(stat) ? 2 : mediumXCheckGroup.includes(stat) ? 1 : lowXCheckGroup.includes(stat) ? 0.5 : 0.25
|
|
// need to regex check the wF% stat to the xcheckcount stat of the same position to check qualifiers
|
|
const position = stat.match(/weightedFieldingPercent(\w+)/)
|
|
if (!position?.length) return []
|
|
|
|
// This is really guaranteed to be the following but I can't figure out how to make TS agree...
|
|
// 'xCheckCountPitcher' | 'xCheckCountCatcher' | 'xCheckCountFirstBase' |
|
|
// 'xCheckCountSecondBase' | 'xCheckCountThirdBase' | 'xCheckCountShortstop' |
|
|
// 'xCheckCountLeftField' | 'xCheckCountCenterField' | 'xCheckCountRightField'
|
|
const xCheckCountProperty = `xCheckCount${position[1]}` as keyof FlatFieldingStat
|
|
|
|
return this.allPlayersFieldingStats.concat([])
|
|
.filter(stat => (stat[xCheckCountProperty] as number) >= Math.floor(xCheckCountPerWeek * this.weekNumberForCalcs))
|
|
.sort((a, b) => ((b[stat] as number) - (a[stat] as number))).slice(0, 10)
|
|
}
|
|
|
|
// non-wF% stats currently won't have any qualifying minimum
|
|
return this.allPlayersFieldingStats.concat([])
|
|
.sort((a, b) => ((b[stat] as number) - (a[stat] as number)))
|
|
.slice(0, 10)
|
|
},
|
|
formatNumericalStat(stat: BattingStat | PitchingStat | FlatFieldingStat, property: keyof BattingStat | keyof PitchingStat | keyof FlatFieldingStat, precision: number): number | string {
|
|
let numericalStat: number = 0
|
|
|
|
if (this.statType == 'Batting') numericalStat = (stat as BattingStat)[property as keyof BattingStat] as number
|
|
if (this.statType == 'Pitching') numericalStat = (stat as PitchingStat)[property as keyof PitchingStat] as number
|
|
if (this.statType == 'Fielding') numericalStat = (stat as FlatFieldingStat)[property as keyof FlatFieldingStat] as number
|
|
|
|
if (Number.isInteger(numericalStat) && !precision) return numericalStat
|
|
|
|
return numericalStat.toFixed(precision)
|
|
},
|
|
async fetchData(): Promise<void> {
|
|
const leagueInfo: LeagueInfo = await fetchCurrentLeagueInfo()
|
|
this.weekNumber = leagueInfo.week
|
|
|
|
if (!this.teamsById.size) {
|
|
const teams = await fetchTeamsBySeason(this.seasonNumber)
|
|
this.teamsById = new Map(teams.map(team => [team.id, team]))
|
|
}
|
|
|
|
if (this.statType == 'Batting') {
|
|
this.allPlayersBattingStats = (await fetchBattingStatsBySeason(this.seasonNumber, true))
|
|
.map(stat => {
|
|
return { ...stat, team: this.teamsById.get(stat.player.team as unknown as number) ?? stat.team }
|
|
})
|
|
.filter(stat => stat.pa > 0)
|
|
}
|
|
if (this.statType == 'Pitching') {
|
|
this.allPlayersPitchingStats = (await fetchPitchingStatsBySeason(this.seasonNumber, true))
|
|
.map(stat => {
|
|
return { ...stat, team: this.teamsById.get(stat.player.team as unknown as number) ?? stat.team }
|
|
})
|
|
.filter(stat => stat.ip > 0)
|
|
}
|
|
if (this.statType == 'Fielding') {
|
|
const allPlayerPositionFieldingStats: FieldingStat[] = (await fetchFieldingStatsBySeason(this.seasonNumber, true))
|
|
.filter(stat => stat.xCheckCount > 0 || stat.stolenBaseCheckCount > 0 || stat.passedBallCount > 0)
|
|
const aggregatedFieldingStatsByPlayer: { [playerName: string]: FlatFieldingStat } = {}
|
|
|
|
allPlayerPositionFieldingStats.forEach(stat => {
|
|
if (!aggregatedFieldingStatsByPlayer[stat.player.name]) {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name] = {
|
|
player: stat.player,
|
|
// looking up team as api provides first team with stat so traded players are incorrect
|
|
team: this.teamsById.get(stat.player.team as unknown as number) ?? stat.team,
|
|
xCheckCount: 0,
|
|
hit: 0,
|
|
error: 0,
|
|
stolenBaseCheckCount: 0,
|
|
stolenBaseCount: 0,
|
|
caughtStealingCount: 0,
|
|
caughtStealingPercent: 0,
|
|
passedBallCount: 0,
|
|
xCheckCountPitcher: 0,
|
|
weightedFieldingPercentPitcher: 0,
|
|
xCheckCountCatcher: 0,
|
|
weightedFieldingPercentCatcher: 0,
|
|
xCheckCountFirstBase: 0,
|
|
weightedFieldingPercentFirstBase: 0,
|
|
xCheckCountSecondBase: 0,
|
|
weightedFieldingPercentSecondBase: 0,
|
|
xCheckCountThirdBase: 0,
|
|
weightedFieldingPercentThirdBase: 0,
|
|
xCheckCountShortstop: 0,
|
|
weightedFieldingPercentShortstop: 0,
|
|
xCheckCountLeftField: 0,
|
|
weightedFieldingPercentLeftField: 0,
|
|
xCheckCountCenterField: 0,
|
|
weightedFieldingPercentCenterField: 0,
|
|
xCheckCountRightField: 0,
|
|
weightedFieldingPercentRightField: 0
|
|
}
|
|
}
|
|
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCount += stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].hit += stat.hit
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].error += stat.error
|
|
|
|
if (stat.pos === 'P') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountPitcher = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentPitcher = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === 'C') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountCatcher = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentCatcher = stat.weightedFieldingPercent
|
|
// catchers have extra stats
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].stolenBaseCheckCount = stat.stolenBaseCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].stolenBaseCount = stat.stolenBaseCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].caughtStealingCount = stat.caughtStealingCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].caughtStealingPercent = stat.caughtStealingPercent
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].passedBallCount = stat.passedBallCount
|
|
}
|
|
else if (stat.pos === '1B') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountFirstBase = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentFirstBase = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === '2B') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountSecondBase = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentSecondBase = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === '3B') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountThirdBase = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentThirdBase = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === 'SS') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountShortstop = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentShortstop = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === 'LF') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountLeftField = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentLeftField = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === 'CF') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountCenterField = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentCenterField = stat.weightedFieldingPercent
|
|
}
|
|
else if (stat.pos === 'RF') {
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].xCheckCountRightField = stat.xCheckCount
|
|
aggregatedFieldingStatsByPlayer[stat.player.name].weightedFieldingPercentRightField = stat.weightedFieldingPercent
|
|
}
|
|
})
|
|
|
|
this.allPlayersFieldingStats = Object.values(aggregatedFieldingStatsByPlayer)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script> |